fraim-hub 2.0.284 → 2.0.286
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/desktop-auto-updater.js +74 -0
- package/dist/src/ai-hub/desktop-main.js +25 -6
- package/dist/src/ai-hub/hosts.js +23 -0
- package/dist/src/ai-hub/server.js +12 -3
- package/dist/src/cli/doctor/checks/agent-cli-health-checks.js +4 -3
- package/dist/src/cli/utils/managed-agent-paths.js +7 -0
- package/dist/src/config/ai-manager-hiring.js +1 -0
- package/dist/src/config/learning-domains.js +1 -0
- package/dist/src/config/persona-capability-bundles.js +18 -2
- package/dist/src/config/persona-hiring.js +123 -113
- package/dist/src/first-run/session-service.js +42 -8
- package/package.json +2 -2
- package/public/ai-hub/script.js +33 -13
- package/public/ai-hub/styles.css +7 -1
- package/public/portfolio/aida.html +243 -0
- package/public/portfolio/index.html +198 -177
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.runMandatoryDesktopUpdateCheck = runMandatoryDesktopUpdateCheck;
|
|
4
|
+
let updateCheckInFlight = null;
|
|
5
|
+
function runMandatoryDesktopUpdateCheck(options) {
|
|
6
|
+
if (!options.isPackaged)
|
|
7
|
+
return Promise.resolve({ action: 'skipped-unpackaged' });
|
|
8
|
+
if (updateCheckInFlight)
|
|
9
|
+
return updateCheckInFlight;
|
|
10
|
+
updateCheckInFlight = runMandatoryDesktopUpdateCheckOnce(options)
|
|
11
|
+
.finally(() => {
|
|
12
|
+
updateCheckInFlight = null;
|
|
13
|
+
});
|
|
14
|
+
return updateCheckInFlight;
|
|
15
|
+
}
|
|
16
|
+
async function runMandatoryDesktopUpdateCheckOnce(options) {
|
|
17
|
+
const { updater, prompt, currentVersion, logger = console } = options;
|
|
18
|
+
updater.autoDownload = false;
|
|
19
|
+
updater.autoInstallOnAppQuit = false;
|
|
20
|
+
const checkResult = await checkForDesktopUpdate(updater, prompt, currentVersion, logger);
|
|
21
|
+
if (checkResult.action === 'check-failed')
|
|
22
|
+
return checkResult;
|
|
23
|
+
if (!checkResult.update?.isUpdateAvailable) {
|
|
24
|
+
logger.info('[fraim] desktop update check found no newer version');
|
|
25
|
+
return { action: 'current', availableVersion: checkResult.update?.updateInfo?.version };
|
|
26
|
+
}
|
|
27
|
+
const availableVersion = checkResult.update.updateInfo?.version;
|
|
28
|
+
try {
|
|
29
|
+
logger.info(`[fraim] desktop update ${availableVersion ?? 'unknown'} available; downloading`);
|
|
30
|
+
if (checkResult.update.downloadPromise) {
|
|
31
|
+
await checkResult.update.downloadPromise;
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
await updater.downloadUpdate();
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
const message = errorMessage(error);
|
|
39
|
+
logger.error(`[fraim] desktop update download failed: ${message}`);
|
|
40
|
+
await promptUpdateFailure(prompt, 'FRAIM Hub update download failed', 'FRAIM Hub found an update but could not download it. Please restart FRAIM Hub or reinstall from the latest installer.', message);
|
|
41
|
+
return { action: 'download-failed', availableVersion, error: message };
|
|
42
|
+
}
|
|
43
|
+
try {
|
|
44
|
+
logger.info(`[fraim] desktop update ${availableVersion ?? 'unknown'} downloaded; installing`);
|
|
45
|
+
updater.quitAndInstall(false, true);
|
|
46
|
+
return { action: 'installing', availableVersion };
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
const message = errorMessage(error);
|
|
50
|
+
logger.error(`[fraim] desktop update install failed: ${message}`);
|
|
51
|
+
await promptUpdateFailure(prompt, 'FRAIM Hub update install failed', 'FRAIM Hub downloaded an update but could not start the installer. Please restart FRAIM Hub or reinstall from the latest installer.', message);
|
|
52
|
+
return { action: 'install-failed', availableVersion, error: message };
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
async function checkForDesktopUpdate(updater, prompt, currentVersion, logger) {
|
|
56
|
+
try {
|
|
57
|
+
logger.info(`[fraim] checking for desktop update from ${currentVersion}`);
|
|
58
|
+
return { action: 'checked', update: await updater.checkForUpdates() };
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
const message = errorMessage(error);
|
|
62
|
+
logger.warn(`[fraim] desktop update check failed: ${message}`);
|
|
63
|
+
await promptUpdateFailure(prompt, 'FRAIM Hub update check failed', 'FRAIM Hub could not check for updates. It will continue starting, but this installed app may be stale.', message);
|
|
64
|
+
return { action: 'check-failed', error: message };
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
function promptUpdateFailure(prompt, title, message, detail) {
|
|
68
|
+
return prompt.showErrorBox(title, message, detail);
|
|
69
|
+
}
|
|
70
|
+
function errorMessage(error) {
|
|
71
|
+
if (error instanceof Error)
|
|
72
|
+
return error.message;
|
|
73
|
+
return String(error);
|
|
74
|
+
}
|
|
@@ -19,6 +19,7 @@ const bundled_asset_resolver_1 = require("./bundled-asset-resolver");
|
|
|
19
19
|
const server_2 = require("../first-run/server");
|
|
20
20
|
const session_service_1 = require("../first-run/session-service");
|
|
21
21
|
const fraim_mcp_latest_launcher_1 = require("../cli/mcp/fraim-mcp-latest-launcher");
|
|
22
|
+
const desktop_auto_updater_1 = require("./desktop-auto-updater");
|
|
22
23
|
// Keep installed, running, and user-pinned Windows shortcuts grouped under the
|
|
23
24
|
// stable identity declared in packages/fraim-hub/package.json.
|
|
24
25
|
electron_1.app.setAppUserModelId('ai.fraim.hub');
|
|
@@ -110,18 +111,33 @@ function ensureLoginItem() {
|
|
|
110
111
|
fs_1.default.mkdirSync(path_1.default.dirname(flagPath), { recursive: true });
|
|
111
112
|
fs_1.default.writeFileSync(flagPath, '1');
|
|
112
113
|
}
|
|
113
|
-
function configureAutoUpdater() {
|
|
114
|
+
async function configureAutoUpdater() {
|
|
114
115
|
if (!electron_1.app.isPackaged)
|
|
115
|
-
return;
|
|
116
|
+
return false;
|
|
116
117
|
// #1110: electron-updater compiles ~114 files (js-yaml, builder-util-runtime, ...) that a
|
|
117
118
|
// non-packaged `npx fraim-hub` launch never uses, and this whole function returns early
|
|
118
119
|
// there. Requiring it lazily keeps those file reads off the cold-start path, which is what
|
|
119
120
|
// dominates time-to-ready on a freshly unpacked install.
|
|
120
121
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
121
122
|
const { autoUpdater } = require('electron-updater');
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
123
|
+
const result = await (0, desktop_auto_updater_1.runMandatoryDesktopUpdateCheck)({
|
|
124
|
+
isPackaged: electron_1.app.isPackaged,
|
|
125
|
+
updater: autoUpdater,
|
|
126
|
+
currentVersion: electron_1.app.getVersion(),
|
|
127
|
+
logger: console,
|
|
128
|
+
prompt: {
|
|
129
|
+
showErrorBox: (title, message, detail) => {
|
|
130
|
+
electron_1.dialog.showErrorBox(title, detail ? `${message}\n\n${detail}` : message);
|
|
131
|
+
},
|
|
132
|
+
},
|
|
133
|
+
});
|
|
134
|
+
return result.action === 'installing';
|
|
135
|
+
}
|
|
136
|
+
function checkForUpdateAfterSecondInstance() {
|
|
137
|
+
if (!electron_1.app.isPackaged || process.env.FRAIM_INSTALLER_LIFECYCLE_TEST === '1')
|
|
138
|
+
return;
|
|
139
|
+
void configureAutoUpdater().catch((err) => {
|
|
140
|
+
console.warn('[fraim] second-instance update check failed:', err);
|
|
125
141
|
});
|
|
126
142
|
}
|
|
127
143
|
// ---------------------------------------------------------------------------
|
|
@@ -467,6 +483,7 @@ async function bootstrap() {
|
|
|
467
483
|
return;
|
|
468
484
|
}
|
|
469
485
|
electron_1.app.on('second-instance', () => {
|
|
486
|
+
void electron_1.app.whenReady().then(checkForUpdateAfterSecondInstance);
|
|
470
487
|
if (mainWindow) {
|
|
471
488
|
mainWindow.show();
|
|
472
489
|
mainWindow.focus();
|
|
@@ -486,7 +503,9 @@ async function bootstrap() {
|
|
|
486
503
|
// First-launch housekeeping (idempotent, fast on subsequent runs)
|
|
487
504
|
if (process.env.FRAIM_INSTALLER_LIFECYCLE_TEST !== '1') {
|
|
488
505
|
ensureLoginItem();
|
|
489
|
-
configureAutoUpdater();
|
|
506
|
+
const installingUpdate = await configureAutoUpdater();
|
|
507
|
+
if (installingUpdate)
|
|
508
|
+
return;
|
|
490
509
|
}
|
|
491
510
|
electron_1.app.on('activate', () => {
|
|
492
511
|
// macOS: clicking dock icon re-shows the window
|
package/dist/src/ai-hub/hosts.js
CHANGED
|
@@ -2071,6 +2071,29 @@ class CliHostRuntime {
|
|
|
2071
2071
|
return null;
|
|
2072
2072
|
return active.pending.length + 1;
|
|
2073
2073
|
}
|
|
2074
|
+
stopActiveSession(hostId, sessionId) {
|
|
2075
|
+
const key = `${hostId}::${sessionId}`;
|
|
2076
|
+
const active = this.activeContinueRuns.get(key);
|
|
2077
|
+
if (!active)
|
|
2078
|
+
return false;
|
|
2079
|
+
active.pending.splice(0);
|
|
2080
|
+
this.activeContinueRuns.delete(key);
|
|
2081
|
+
if (active.child.pid == null)
|
|
2082
|
+
return false;
|
|
2083
|
+
try {
|
|
2084
|
+
this.killTree(active.child.pid, 'SIGTERM');
|
|
2085
|
+
return true;
|
|
2086
|
+
}
|
|
2087
|
+
catch (error) {
|
|
2088
|
+
console.warn('[ai-hub] failed to stop active host session process tree:', {
|
|
2089
|
+
hostId,
|
|
2090
|
+
sessionId,
|
|
2091
|
+
pid: active.child.pid,
|
|
2092
|
+
error: error instanceof Error ? error.message : String(error),
|
|
2093
|
+
});
|
|
2094
|
+
return false;
|
|
2095
|
+
}
|
|
2096
|
+
}
|
|
2074
2097
|
guardedContinue(hostId, sessionId, entry) {
|
|
2075
2098
|
const key = `${hostId}::${sessionId}`;
|
|
2076
2099
|
const active = this.activeContinueRuns.get(key);
|
|
@@ -417,7 +417,12 @@ class AiHubRunRegistry {
|
|
|
417
417
|
(0, tree_kill_1.default)(child.pid, 'SIGTERM');
|
|
418
418
|
return true;
|
|
419
419
|
}
|
|
420
|
-
catch {
|
|
420
|
+
catch (error) {
|
|
421
|
+
console.warn('[ai-hub] failed to stop run process tree:', {
|
|
422
|
+
runId,
|
|
423
|
+
pid: child.pid,
|
|
424
|
+
error: error instanceof Error ? error.message : String(error),
|
|
425
|
+
});
|
|
421
426
|
return false;
|
|
422
427
|
}
|
|
423
428
|
}
|
|
@@ -5945,7 +5950,11 @@ class AiHubServer {
|
|
|
5945
5950
|
return res.json(this.enrichRunForResponse(run));
|
|
5946
5951
|
}
|
|
5947
5952
|
this.runRegistry.update(run.id, (current) => { current.stoppedByUser = true; });
|
|
5948
|
-
const
|
|
5953
|
+
const killedRunChild = this.runRegistry.stop(run.id);
|
|
5954
|
+
const killedHostSession = run.sessionId
|
|
5955
|
+
? this.hostRuntime.stopActiveSession?.(run.hostId, run.sessionId) === true
|
|
5956
|
+
: false;
|
|
5957
|
+
const killed = killedRunChild || killedHostSession;
|
|
5949
5958
|
// Park it immediately (don't wait for onExit, which may lag or not fire on a
|
|
5950
5959
|
// host that already detached). onExit, if it fires, keeps this same state.
|
|
5951
5960
|
this.runRegistry.update(run.id, (current) => {
|
|
@@ -7309,7 +7318,7 @@ class AiHubServer {
|
|
|
7309
7318
|
const delay = recoveryBackoffMs(attempt);
|
|
7310
7319
|
const tid = setTimeout(() => {
|
|
7311
7320
|
const current = this.runRegistry.get(runId);
|
|
7312
|
-
if (!current || current.status !== 'running')
|
|
7321
|
+
if (!current || current.status !== 'running' || current.stoppedByUser)
|
|
7313
7322
|
return;
|
|
7314
7323
|
const message = classification.recoveryKind === 'compaction'
|
|
7315
7324
|
? buildHubCompactionRecoveryContinueMessage(current, exitCode, attempt)
|
|
@@ -15,11 +15,12 @@ const child_process_1 = require("child_process");
|
|
|
15
15
|
const path_1 = __importDefault(require("path"));
|
|
16
16
|
const managed_agent_paths_1 = require("../../utils/managed-agent-paths");
|
|
17
17
|
const command_resolution_1 = require("../../mcp/command-resolution");
|
|
18
|
-
//
|
|
19
|
-
//
|
|
20
|
-
//
|
|
18
|
+
// CLIs with a managed-install fallback (npm install -g into FRAIM's portable
|
|
19
|
+
// Node when no system install is found). Add gemini/copilot here when their
|
|
20
|
+
// installManagedAgent wiring lands.
|
|
21
21
|
const MANAGED_CLIS = [
|
|
22
22
|
{ id: 'codex', label: 'Codex', command: 'codex' },
|
|
23
|
+
{ id: 'claude-code', label: 'Claude Code', command: 'claude' },
|
|
23
24
|
];
|
|
24
25
|
// Windows cannot CreateProcess a `.cmd`/`.bat` file directly (spawnSync on a
|
|
25
26
|
// resolved absolute `.cmd` path throws EINVAL) — it must go through cmd.exe,
|
|
@@ -83,6 +83,13 @@ function cleanupOrphanedManagedShims() {
|
|
|
83
83
|
const removed = [];
|
|
84
84
|
for (const basename of basenames) {
|
|
85
85
|
const candidate = path_1.default.join(nodeRoot, basename);
|
|
86
|
+
// Guard: only delete the flat-dir shim once the versioned dir already has
|
|
87
|
+
// an equivalent. Without this, upgrading users lose their shims before a
|
|
88
|
+
// reinstall has had a chance to place them in the versioned dir — the
|
|
89
|
+
// agent CLI disappears entirely until the next explicit reinstall.
|
|
90
|
+
const versionedEquiv = path_1.default.join(versionedDir, basename);
|
|
91
|
+
if (!fs_1.default.existsSync(versionedEquiv))
|
|
92
|
+
continue;
|
|
86
93
|
try {
|
|
87
94
|
if (fs_1.default.statSync(candidate).isFile()) {
|
|
88
95
|
fs_1.default.unlinkSync(candidate);
|
|
@@ -31,6 +31,7 @@ const GENERALIST_PROFILE = {
|
|
|
31
31
|
};
|
|
32
32
|
/** Role key -> the human manager best suited to manage that AI employee. Mirror of registry/scripts/ai-manager-hiring.ts. */
|
|
33
33
|
exports.HUMAN_MANAGER_PROFILES = {
|
|
34
|
+
aida: { humanTitle: 'Head of AI Engineering', keywords: ['"Head of AI Engineering"', '"Director of AI"', '"AI Engineering Manager"', '"VP Engineering"'] },
|
|
34
35
|
maestro: { humanTitle: 'Co-Founder / General Manager', keywords: ['"Co-Founder"', '"General Manager"', '"Chief of Staff"', '"Founder"'] },
|
|
35
36
|
beza: { humanTitle: 'Head of Strategy', keywords: ['"Head of Strategy"', '"Strategy Director"', '"Chief of Staff"'] },
|
|
36
37
|
pam: { humanTitle: 'Head of Product', keywords: ['"Head of Product"', '"Group Product Manager"', '"Director of Product"'] },
|
|
@@ -63,7 +63,7 @@ exports.PERSONA_CAPABILITY_BUNDLES = {
|
|
|
63
63
|
personaKey: 'swen',
|
|
64
64
|
bundleId: 'persona-swen-core',
|
|
65
65
|
catalogMetadata: buildCatalogMetadata('swen', ['feature-implementation', 'technical-design', 'code-refactoring']),
|
|
66
|
-
protectedJobs: ['feature-implementation', 'technical-design', 'implementation-design-review', 'code-refactoring', 'pr-iteration', 'mobile-app-development', '
|
|
66
|
+
protectedJobs: ['feature-implementation', 'technical-design', 'implementation-design-review', 'code-refactoring', 'pr-iteration', 'mobile-app-development', 'cloud-application-deployment', 'cloud-cost-optimization', 'cloud-performance-diagnosis', 'route-llm-spend-to-cloud-credits', 'set-up-cloud-cost-alerts', 'gitlabs-to-github', 'system-migration', 'cross-cloud-migration', 'data-pipeline-design', 'data-quality-monitoring', 'data-platform-architecture', 'write-dev-docs', 'database-schema-design', 'create-architecture', 'project-scaffolding', 'codebase-analysis-and-ideation', 'github-org-setup', 'google-workspace-setup', 'mobile-app-rejection-response', 'mobile-app-submission', 'application-replication-workflow'],
|
|
67
67
|
protectedAliases: ['software-engineering', 'implementation'],
|
|
68
68
|
defaultHireMode: 'job',
|
|
69
69
|
lockCopy: 'Hire SWEn to unlock software-engineering delivery for this request.'
|
|
@@ -262,6 +262,23 @@ exports.PERSONA_CAPABILITY_BUNDLES = {
|
|
|
262
262
|
protectedAliases: ['banking-audit', 'kyc-audit', 'audit'],
|
|
263
263
|
defaultHireMode: 'job',
|
|
264
264
|
lockCopy: 'Hire AUDITya to unlock banking evidence audit work for this request.'
|
|
265
|
+
},
|
|
266
|
+
aida: {
|
|
267
|
+
personaKey: 'aida',
|
|
268
|
+
bundleId: 'persona-aida-core',
|
|
269
|
+
catalogMetadata: buildCatalogMetadata('aida', ['create-ai-agent', 'author-ai-evals', 'enable-web-mcp']),
|
|
270
|
+
protectedJobs: [
|
|
271
|
+
'create-ai-agent',
|
|
272
|
+
'author-ai-evals',
|
|
273
|
+
'enable-web-mcp',
|
|
274
|
+
'evaluate-ai-agent',
|
|
275
|
+
'mcp-server-creation',
|
|
276
|
+
'publish-mcp-app',
|
|
277
|
+
'create-hub-configured-agent',
|
|
278
|
+
],
|
|
279
|
+
protectedAliases: ['ai-engineering', 'agent-engineering', 'ai-agents'],
|
|
280
|
+
defaultHireMode: 'job',
|
|
281
|
+
lockCopy: 'Hire AIda to unlock AI agent design, MCP enablement, eval authoring, and agent evaluation work for this request.'
|
|
265
282
|
}
|
|
266
283
|
};
|
|
267
284
|
const PROTECTED_JOB_TO_PERSONA = new Map();
|
|
@@ -275,7 +292,6 @@ for (const bundle of Object.values(exports.PERSONA_CAPABILITY_BUNDLES)) {
|
|
|
275
292
|
// as "free") so the Hub attributes them to FRAIMworker, but they are never
|
|
276
293
|
// hire-gated because FRAIMworker is not a purchasable persona.
|
|
277
294
|
const GENERIC_WORKER_OWNED_JOBS = new Set([
|
|
278
|
-
'create-hub-configured-agent',
|
|
279
295
|
'contribute-to-fraim',
|
|
280
296
|
'file-fraim-issue',
|
|
281
297
|
'praise-fraim',
|
|
@@ -19,32 +19,14 @@ exports.getPersonaHireAmountCents = getPersonaHireAmountCents;
|
|
|
19
19
|
* GET /api/personas/catalog.
|
|
20
20
|
*/
|
|
21
21
|
exports.PERSONA_HIRE_CATALOG = {
|
|
22
|
-
|
|
23
|
-
displayName: '
|
|
24
|
-
role: '
|
|
25
|
-
emoji: '
|
|
26
|
-
gradient: 'linear-gradient(135deg, #
|
|
27
|
-
blurb: '
|
|
28
|
-
jobPriceCents:
|
|
29
|
-
fulltimePriceCents:
|
|
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,
|
|
22
|
+
aida: {
|
|
23
|
+
displayName: 'AIda',
|
|
24
|
+
role: 'AI Engineer',
|
|
25
|
+
emoji: '\u{1F9E0}',
|
|
26
|
+
gradient: 'linear-gradient(135deg, #4f46e5 0%, #06b6d4 50%, #10b981 100%)',
|
|
27
|
+
blurb: 'Designs production AI agents, connects them to tools and data, and proves their behavior with evals before deployment.',
|
|
28
|
+
jobPriceCents: 1290, // placeholder — product owner approval required before launch
|
|
29
|
+
fulltimePriceCents: 5990, // placeholder — product owner approval required before launch
|
|
48
30
|
},
|
|
49
31
|
swen: {
|
|
50
32
|
displayName: 'SWEn',
|
|
@@ -64,33 +46,6 @@ exports.PERSONA_HIRE_CATALOG = {
|
|
|
64
46
|
jobPriceCents: 590,
|
|
65
47
|
fulltimePriceCents: 3990,
|
|
66
48
|
},
|
|
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
49
|
sekhar: {
|
|
95
50
|
displayName: 'SEChar',
|
|
96
51
|
role: 'AI Security Engineer',
|
|
@@ -100,51 +55,15 @@ exports.PERSONA_HIRE_CATALOG = {
|
|
|
100
55
|
jobPriceCents: 1790,
|
|
101
56
|
fulltimePriceCents: 6490,
|
|
102
57
|
},
|
|
103
|
-
|
|
104
|
-
displayName: '
|
|
105
|
-
role: 'AI
|
|
106
|
-
emoji: '
|
|
107
|
-
gradient: 'linear-gradient(135deg, #
|
|
108
|
-
blurb: '
|
|
109
|
-
jobPriceCents:
|
|
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,
|
|
58
|
+
sreya: {
|
|
59
|
+
displayName: 'SREya',
|
|
60
|
+
role: 'AI Site Reliability Engineer',
|
|
61
|
+
emoji: '☁️',
|
|
62
|
+
gradient: 'linear-gradient(135deg, #2563eb 0%, #10b981 100%)',
|
|
63
|
+
blurb: 'Manages deployments, monitors uptime, optimizes cloud cost, and keeps infrastructure resilient and observable.',
|
|
64
|
+
jobPriceCents: 1290,
|
|
119
65
|
fulltimePriceCents: 5990,
|
|
120
66
|
},
|
|
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
67
|
sade: {
|
|
149
68
|
displayName: 'SADE',
|
|
150
69
|
role: 'AI Salesforce Developer',
|
|
@@ -154,6 +73,33 @@ exports.PERSONA_HIRE_CATALOG = {
|
|
|
154
73
|
jobPriceCents: 1490,
|
|
155
74
|
fulltimePriceCents: 6490,
|
|
156
75
|
},
|
|
76
|
+
pam: {
|
|
77
|
+
displayName: 'PaM',
|
|
78
|
+
role: 'AI Product Manager',
|
|
79
|
+
emoji: '📋',
|
|
80
|
+
gradient: 'linear-gradient(135deg, #8b5cf6 0%, #d946ef 100%)',
|
|
81
|
+
blurb: 'Owns specs, PRDs, technical design, issue prep, and the path from idea to shippable artifact.',
|
|
82
|
+
jobPriceCents: 790,
|
|
83
|
+
fulltimePriceCents: 4990,
|
|
84
|
+
},
|
|
85
|
+
huxley: {
|
|
86
|
+
displayName: 'hUXley',
|
|
87
|
+
role: 'AI UX / Brand Designer',
|
|
88
|
+
emoji: '🎨',
|
|
89
|
+
gradient: 'linear-gradient(135deg, #ec4899 0%, #f472b6 100%)',
|
|
90
|
+
blurb: 'Builds design systems, prototypes polished user-facing surfaces, and carries brand decisions into shipped product experiences.',
|
|
91
|
+
jobPriceCents: 1490,
|
|
92
|
+
fulltimePriceCents: 5490,
|
|
93
|
+
},
|
|
94
|
+
gautam: {
|
|
95
|
+
displayName: 'GauTaM',
|
|
96
|
+
role: 'AI GTM & Marketing Manager',
|
|
97
|
+
emoji: '📣',
|
|
98
|
+
gradient: 'linear-gradient(135deg, #f97316 0%, #f59e0b 100%)',
|
|
99
|
+
blurb: 'Defines marketing strategy, ships content, runs launches, and owns the brand voice in market.',
|
|
100
|
+
jobPriceCents: 890,
|
|
101
|
+
fulltimePriceCents: 4990,
|
|
102
|
+
},
|
|
157
103
|
sam: {
|
|
158
104
|
displayName: 'SAM',
|
|
159
105
|
role: 'AI Sales Account Manager',
|
|
@@ -172,15 +118,6 @@ exports.PERSONA_HIRE_CATALOG = {
|
|
|
172
118
|
jobPriceCents: 990,
|
|
173
119
|
fulltimePriceCents: 4990,
|
|
174
120
|
},
|
|
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
121
|
mona: {
|
|
185
122
|
displayName: 'MONa',
|
|
186
123
|
role: 'AI Finance Manager',
|
|
@@ -190,14 +127,32 @@ exports.PERSONA_HIRE_CATALOG = {
|
|
|
190
127
|
jobPriceCents: 990,
|
|
191
128
|
fulltimePriceCents: 4990,
|
|
192
129
|
},
|
|
193
|
-
|
|
194
|
-
displayName: '
|
|
195
|
-
role: 'AI
|
|
196
|
-
emoji: '
|
|
197
|
-
gradient: 'linear-gradient(135deg, #
|
|
198
|
-
blurb: 'Manages
|
|
199
|
-
jobPriceCents:
|
|
200
|
-
fulltimePriceCents:
|
|
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
|
+
ricardo: {
|
|
140
|
+
displayName: 'RECardo',
|
|
141
|
+
role: 'AI Recruiter',
|
|
142
|
+
emoji: '🤝',
|
|
143
|
+
gradient: 'linear-gradient(135deg, #6366f1 0%, #ec4899 100%)',
|
|
144
|
+
blurb: 'Sources candidates, writes job descriptions, screens pipelines, and manages the hiring loop end-to-end.',
|
|
145
|
+
jobPriceCents: 790,
|
|
146
|
+
fulltimePriceCents: 4490,
|
|
147
|
+
},
|
|
148
|
+
cela: {
|
|
149
|
+
displayName: 'CELiA',
|
|
150
|
+
role: 'AI Legal Counsel',
|
|
151
|
+
emoji: '⚖️',
|
|
152
|
+
gradient: 'linear-gradient(135deg, #475569 0%, #6366f1 100%)',
|
|
153
|
+
blurb: 'Drafts and reviews contracts, NDAs, patents, trademarks, and the SaaS legal stack.',
|
|
154
|
+
jobPriceCents: 1990,
|
|
155
|
+
fulltimePriceCents: 6990,
|
|
201
156
|
},
|
|
202
157
|
procella: {
|
|
203
158
|
displayName: 'PROCella',
|
|
@@ -226,6 +181,60 @@ exports.PERSONA_HIRE_CATALOG = {
|
|
|
226
181
|
jobPriceCents: 1490,
|
|
227
182
|
fulltimePriceCents: 6490,
|
|
228
183
|
},
|
|
184
|
+
deidre: {
|
|
185
|
+
displayName: 'DEIdre',
|
|
186
|
+
role: 'AI Inclusion Leader',
|
|
187
|
+
emoji: '🌍',
|
|
188
|
+
gradient: 'linear-gradient(135deg, #9333ea 0%, #d946ef 100%)',
|
|
189
|
+
blurb: 'Audits equity gaps, designs bias-aware AI governance, builds ERG toolkits, and creates inclusion-fluency programs.',
|
|
190
|
+
jobPriceCents: 890,
|
|
191
|
+
fulltimePriceCents: 4490,
|
|
192
|
+
},
|
|
193
|
+
careena: {
|
|
194
|
+
displayName: 'CAREEna',
|
|
195
|
+
role: 'AI Career Coach',
|
|
196
|
+
emoji: '🎓',
|
|
197
|
+
gradient: 'linear-gradient(135deg, #0ea5e9 0%, #6366f1 100%)',
|
|
198
|
+
blurb: 'Runs the candidate-side search loop: role sourcing, application execution, networking, interview prep, and close-stage offer strategy.',
|
|
199
|
+
jobPriceCents: 890,
|
|
200
|
+
fulltimePriceCents: 4990,
|
|
201
|
+
},
|
|
202
|
+
ashley: {
|
|
203
|
+
displayName: 'AshLey',
|
|
204
|
+
role: 'AI Executive Assistant',
|
|
205
|
+
emoji: '📅',
|
|
206
|
+
gradient: 'linear-gradient(135deg, #f59e0b 0%, #fbbf24 100%)',
|
|
207
|
+
blurb: 'Owns executive coordination, weekly operating reviews, and portfolio reporting across the workforce.',
|
|
208
|
+
jobPriceCents: 490,
|
|
209
|
+
fulltimePriceCents: 2990,
|
|
210
|
+
},
|
|
211
|
+
mandy: {
|
|
212
|
+
displayName: 'MANdy',
|
|
213
|
+
role: 'AI Manager',
|
|
214
|
+
emoji: '🎯',
|
|
215
|
+
gradient: 'linear-gradient(135deg, #7c3aed 0%, #4338ca 100%)',
|
|
216
|
+
blurb: 'Plans the job sequence, runs sub-agents in parallel, coaches them through verification loops, and hands back a synthesized DRAFT for your approval.',
|
|
217
|
+
jobPriceCents: 1490,
|
|
218
|
+
fulltimePriceCents: 5990,
|
|
219
|
+
},
|
|
220
|
+
beza: {
|
|
221
|
+
displayName: 'BeZa',
|
|
222
|
+
role: 'AI Business Strategist',
|
|
223
|
+
emoji: '🧭',
|
|
224
|
+
gradient: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)',
|
|
225
|
+
blurb: 'Turns ideas into structured business plans, validates founder-market fit, and pressure-tests strategy.',
|
|
226
|
+
jobPriceCents: 990,
|
|
227
|
+
fulltimePriceCents: 4990,
|
|
228
|
+
},
|
|
229
|
+
maestro: {
|
|
230
|
+
displayName: 'MAESTRO',
|
|
231
|
+
role: 'Full-Brained AI Employee',
|
|
232
|
+
emoji: '★',
|
|
233
|
+
gradient: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 50%, #d946ef 100%)',
|
|
234
|
+
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.',
|
|
235
|
+
jobPriceCents: 24900, // $249 — full-time only; job mode maps to same price
|
|
236
|
+
fulltimePriceCents: 24900, // $249/mo
|
|
237
|
+
},
|
|
229
238
|
};
|
|
230
239
|
exports.PERSONA_AVATAR_CATALOG = {
|
|
231
240
|
maestro: { seed: 'MAESTRO-founder-mode', bg: 'fde68a', style: 'notionists' },
|
|
@@ -251,6 +260,7 @@ exports.PERSONA_AVATAR_CATALOG = {
|
|
|
251
260
|
procella: { seed: 'PROCELLA-procurement', bg: 'ccfbf1', style: 'notionists' },
|
|
252
261
|
banke: { seed: 'BANKe-banking-kyc', bg: 'ccfbf1', style: 'notionists' },
|
|
253
262
|
auditya: { seed: 'AUDITya-banking-audit', bg: 'e9d5ff', style: 'notionists' },
|
|
263
|
+
aida: { seed: 'AIda-ai-engineer', bg: 'c7d2fe', style: 'notionists' },
|
|
254
264
|
};
|
|
255
265
|
function buildPersonaAvatarUrl(personaKey) {
|
|
256
266
|
const avatar = exports.PERSONA_AVATAR_CATALOG[personaKey];
|