fraim-hub 2.0.204
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/README.md +467 -0
- package/bin/fraim-hub.js +17 -0
- package/dist/src/ai-hub/brand-store.js +17 -0
- package/dist/src/ai-hub/catalog.js +412 -0
- package/dist/src/ai-hub/cert-store.js +70 -0
- package/dist/src/ai-hub/cli.js +212 -0
- package/dist/src/ai-hub/configured-agents.js +283 -0
- package/dist/src/ai-hub/conversation-store.js +428 -0
- package/dist/src/ai-hub/desktop-main.js +449 -0
- package/dist/src/ai-hub/hosts.js +1669 -0
- package/dist/src/ai-hub/hub-latest-version.js +52 -0
- package/dist/src/ai-hub/hub-launch-decision.js +57 -0
- package/dist/src/ai-hub/hub-runtime-file.js +43 -0
- package/dist/src/ai-hub/managed-browser.js +269 -0
- package/dist/src/ai-hub/manager-turns.js +64 -0
- package/dist/src/ai-hub/office-sideload.js +156 -0
- package/dist/src/ai-hub/openclaw-bridge.js +250 -0
- package/dist/src/ai-hub/preferences.js +201 -0
- package/dist/src/ai-hub/remote-hub-gateway.js +88 -0
- package/dist/src/ai-hub/server.js +4013 -0
- package/dist/src/ai-hub/types.js +2 -0
- package/dist/src/ai-hub/url-safety.js +19 -0
- package/dist/src/api/admin/payments.js +33 -0
- package/dist/src/api/admin/sales-leads.js +21 -0
- package/dist/src/api/payment/create-session.js +338 -0
- package/dist/src/api/payment/dashboard-link.js +149 -0
- package/dist/src/api/payment/session-details.js +31 -0
- package/dist/src/api/payment/webhook.js +587 -0
- package/dist/src/api/sales/contact.js +44 -0
- package/dist/src/cli/api/get-provider-client.js +41 -0
- package/dist/src/cli/api/provider-client.js +107 -0
- package/dist/src/cli/commands/add-ide.js +462 -0
- package/dist/src/cli/fraim-hub.js +50 -0
- package/dist/src/cli/internal/device-flow-service.js +83 -0
- package/dist/src/cli/mcp/command-resolution.js +81 -0
- package/dist/src/cli/mcp/fraim-mcp-latest-launcher.js +136 -0
- package/dist/src/cli/mcp/ide-formats.js +317 -0
- package/dist/src/cli/mcp/mcp-server-builder.js +48 -0
- package/dist/src/cli/mcp/mcp-server-registry.js +173 -0
- package/dist/src/cli/providers/local-provider-registry.js +165 -0
- package/dist/src/cli/providers/provider-registry.js +230 -0
- package/dist/src/cli/setup/claude-code-telemetry.js +59 -0
- package/dist/src/cli/setup/codex-local-config.js +37 -0
- package/dist/src/cli/setup/ide-detector.js +383 -0
- package/dist/src/cli/setup/ide-global-integration.js +100 -0
- package/dist/src/cli/setup/ide-invocation-surfaces.js +160 -0
- package/dist/src/cli/setup/mcp-config-generator.js +225 -0
- package/dist/src/cli/setup/provider-prompts.js +339 -0
- package/dist/src/cli/utils/managed-agent-paths.js +118 -0
- package/dist/src/cli/utils/script-sync-utils.js +221 -0
- package/dist/src/cli/utils/user-config.js +100 -0
- package/dist/src/cli/utils/version-utils.js +35 -0
- package/dist/src/config/ai-manager-hiring.js +121 -0
- package/dist/src/config/feature-flags.js +25 -0
- package/dist/src/config/persona-capability-bundles.js +277 -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/brand-store.js +232 -0
- package/dist/src/core/quality-evidence.js +331 -0
- package/dist/src/core/utils/git-utils.js +194 -0
- package/dist/src/core/utils/ports.js +32 -0
- package/dist/src/core/utils/project-fraim-paths.js +220 -0
- package/dist/src/db/payment-repository.js +61 -0
- package/dist/src/first-run/types.js +93 -0
- package/dist/src/fraim/db-service.js +2409 -0
- package/dist/src/local-mcp-server/agent-token-prices.js +167 -0
- package/dist/src/local-mcp-server/learning-context-builder.js +975 -0
- package/dist/src/middleware/rate-limit.js +110 -0
- package/dist/src/models/payment.js +2 -0
- package/dist/src/routes/payment-routes.js +186 -0
- package/dist/src/routes/persona-catalog-routes.js +84 -0
- package/dist/src/services/dashboard-access.js +27 -0
- package/dist/src/services/email-service.js +951 -0
- package/dist/src/services/persona-entitlement-service.js +352 -0
- package/dist/src/services/workspace-identity.js +21 -0
- package/dist/src/types/analytics.js +2 -0
- package/dist/src/utils/payment-calculator.js +52 -0
- package/extensions/office-word/favicon.ico +0 -0
- package/extensions/office-word/icon-64.png +0 -0
- package/extensions/office-word/manifest.xml +33 -0
- package/extensions/office-word/taskpane.html +242 -0
- package/index.js +85 -0
- package/package.json +106 -0
- package/public/ai-hub/index.html +910 -0
- package/public/ai-hub/powerpoint-taskpane/icon-64.png +0 -0
- package/public/ai-hub/powerpoint-taskpane/index.html +236 -0
- package/public/ai-hub/powerpoint-taskpane/manifest.xml +30 -0
- package/public/ai-hub/review.css +444 -0
- package/public/ai-hub/script.js +11737 -0
- package/public/ai-hub/styles.css +5102 -0
- package/public/first-run/error-frame.js +100 -0
- package/public/first-run/index.html +35 -0
- package/public/first-run/script.js +739 -0
- package/public/first-run/styles.css +929 -0
- 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,221 @@
|
|
|
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.getUserFraimDir = getUserFraimDir;
|
|
7
|
+
exports.getUserScriptsDir = getUserScriptsDir;
|
|
8
|
+
exports.ensureUserFraimDirectories = ensureUserFraimDirectories;
|
|
9
|
+
exports.getRegistryScripts = getRegistryScripts;
|
|
10
|
+
exports.copyScriptToUserDirectory = copyScriptToUserDirectory;
|
|
11
|
+
exports.syncScriptsToUserDirectory = syncScriptsToUserDirectory;
|
|
12
|
+
exports.cleanupObsoleteUserScripts = cleanupObsoleteUserScripts;
|
|
13
|
+
exports.getUserScriptPath = getUserScriptPath;
|
|
14
|
+
exports.userScriptExists = userScriptExists;
|
|
15
|
+
exports.validateUserScripts = validateUserScripts;
|
|
16
|
+
const fs_1 = __importDefault(require("fs"));
|
|
17
|
+
const path_1 = __importDefault(require("path"));
|
|
18
|
+
const os_1 = __importDefault(require("os"));
|
|
19
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
20
|
+
/**
|
|
21
|
+
* Get the user-level FRAIM directory path
|
|
22
|
+
* Can be overridden with FRAIM_USER_DIR environment variable for testing
|
|
23
|
+
*/
|
|
24
|
+
function getUserFraimDir() {
|
|
25
|
+
return process.env.FRAIM_USER_DIR || path_1.default.join(os_1.default.homedir(), '.fraim');
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Get the user-level scripts directory path
|
|
29
|
+
*/
|
|
30
|
+
function getUserScriptsDir() {
|
|
31
|
+
return path_1.default.join(getUserFraimDir(), 'scripts');
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Ensure the user-level FRAIM directories exist and initialize user config
|
|
35
|
+
*/
|
|
36
|
+
function ensureUserFraimDirectories() {
|
|
37
|
+
const userFraimDir = getUserFraimDir();
|
|
38
|
+
const userScriptsDir = getUserScriptsDir();
|
|
39
|
+
if (!fs_1.default.existsSync(userFraimDir)) {
|
|
40
|
+
fs_1.default.mkdirSync(userFraimDir, { recursive: true });
|
|
41
|
+
}
|
|
42
|
+
if (!fs_1.default.existsSync(userScriptsDir)) {
|
|
43
|
+
fs_1.default.mkdirSync(userScriptsDir, { recursive: true });
|
|
44
|
+
}
|
|
45
|
+
// Initialize user config (~/.fraim/config.json) if it doesn't exist
|
|
46
|
+
const userConfigPath = path_1.default.join(userFraimDir, 'config.json');
|
|
47
|
+
if (!fs_1.default.existsSync(userConfigPath)) {
|
|
48
|
+
fs_1.default.writeFileSync(userConfigPath, JSON.stringify({ workingStyle: 'PR' }, null, 2));
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Get all script files from the registry (recursively)
|
|
53
|
+
*/
|
|
54
|
+
function getRegistryScripts(registryPath) {
|
|
55
|
+
const scriptsPath = path_1.default.join(registryPath, 'scripts');
|
|
56
|
+
if (!fs_1.default.existsSync(scriptsPath)) {
|
|
57
|
+
return [];
|
|
58
|
+
}
|
|
59
|
+
const getAllFiles = (dir, baseDir = dir) => {
|
|
60
|
+
const entries = fs_1.default.readdirSync(dir, { withFileTypes: true });
|
|
61
|
+
let files = [];
|
|
62
|
+
for (const entry of entries) {
|
|
63
|
+
const fullPath = path_1.default.join(dir, entry.name);
|
|
64
|
+
if (entry.isDirectory()) {
|
|
65
|
+
// Recursively get files from subdirectories
|
|
66
|
+
files = files.concat(getAllFiles(fullPath, baseDir));
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
// Include all files (not just specific extensions)
|
|
70
|
+
files.push(fullPath);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return files;
|
|
74
|
+
};
|
|
75
|
+
return getAllFiles(scriptsPath);
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Copy a script file to the user scripts directory with proper permissions
|
|
79
|
+
* Preserves directory structure from registry/scripts/
|
|
80
|
+
*/
|
|
81
|
+
function copyScriptToUserDirectory(sourcePath, registryScriptsPath) {
|
|
82
|
+
const userScriptsDir = getUserScriptsDir();
|
|
83
|
+
// Calculate relative path from registry/scripts to preserve structure
|
|
84
|
+
const relativePath = path_1.default.relative(registryScriptsPath, sourcePath);
|
|
85
|
+
const targetPath = path_1.default.join(userScriptsDir, relativePath);
|
|
86
|
+
// Ensure target directory exists
|
|
87
|
+
const targetDir = path_1.default.dirname(targetPath);
|
|
88
|
+
if (!fs_1.default.existsSync(targetDir)) {
|
|
89
|
+
fs_1.default.mkdirSync(targetDir, { recursive: true });
|
|
90
|
+
}
|
|
91
|
+
// Copy the file
|
|
92
|
+
fs_1.default.copyFileSync(sourcePath, targetPath);
|
|
93
|
+
// Set executable permissions on Unix systems for shell scripts
|
|
94
|
+
if (process.platform !== 'win32' && sourcePath.endsWith('.sh')) {
|
|
95
|
+
try {
|
|
96
|
+
fs_1.default.chmodSync(targetPath, 0o755);
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
console.warn(chalk_1.default.yellow(`⚠️ Could not set executable permissions for ${relativePath}`));
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Sync all scripts from registry to user directory
|
|
105
|
+
*/
|
|
106
|
+
function syncScriptsToUserDirectory(registryPath) {
|
|
107
|
+
ensureUserFraimDirectories();
|
|
108
|
+
const registryScriptsPath = path_1.default.join(registryPath, 'scripts');
|
|
109
|
+
const allScripts = getRegistryScripts(registryPath);
|
|
110
|
+
let syncedCount = 0;
|
|
111
|
+
console.log(chalk_1.default.gray(` Syncing all scripts to ~/.fraim/scripts/:`));
|
|
112
|
+
for (const scriptPath of allScripts) {
|
|
113
|
+
const relativePath = path_1.default.relative(registryScriptsPath, scriptPath);
|
|
114
|
+
const userScriptPath = path_1.default.join(getUserScriptsDir(), relativePath);
|
|
115
|
+
try {
|
|
116
|
+
// Check if script needs updating
|
|
117
|
+
let needsUpdate = true;
|
|
118
|
+
if (fs_1.default.existsSync(userScriptPath)) {
|
|
119
|
+
const registryContent = fs_1.default.readFileSync(scriptPath, 'utf-8');
|
|
120
|
+
const userContent = fs_1.default.readFileSync(userScriptPath, 'utf-8');
|
|
121
|
+
needsUpdate = registryContent !== userContent;
|
|
122
|
+
}
|
|
123
|
+
if (needsUpdate) {
|
|
124
|
+
copyScriptToUserDirectory(scriptPath, registryScriptsPath);
|
|
125
|
+
syncedCount++;
|
|
126
|
+
console.log(chalk_1.default.gray(` + ${relativePath}`));
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
console.log(chalk_1.default.gray(` = ${relativePath} (up to date)`));
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
catch (error) {
|
|
133
|
+
console.warn(chalk_1.default.yellow(`⚠️ Could not sync script ${relativePath}: ${error}`));
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return { synced: syncedCount, ephemeral: 0 };
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Clean up scripts in user directory that no longer exist in registry
|
|
140
|
+
*/
|
|
141
|
+
function cleanupObsoleteUserScripts(registryPath) {
|
|
142
|
+
const userScriptsDir = getUserScriptsDir();
|
|
143
|
+
if (!fs_1.default.existsSync(userScriptsDir)) {
|
|
144
|
+
return 0;
|
|
145
|
+
}
|
|
146
|
+
const registryScriptsPath = path_1.default.join(registryPath, 'scripts');
|
|
147
|
+
const registryScripts = getRegistryScripts(registryPath);
|
|
148
|
+
const registryRelativePaths = registryScripts.map(scriptPath => path_1.default.relative(registryScriptsPath, scriptPath));
|
|
149
|
+
// Get all files in user scripts directory recursively
|
|
150
|
+
const getAllUserFiles = (dir, baseDir = dir) => {
|
|
151
|
+
if (!fs_1.default.existsSync(dir))
|
|
152
|
+
return [];
|
|
153
|
+
const entries = fs_1.default.readdirSync(dir, { withFileTypes: true });
|
|
154
|
+
let files = [];
|
|
155
|
+
for (const entry of entries) {
|
|
156
|
+
const fullPath = path_1.default.join(dir, entry.name);
|
|
157
|
+
if (entry.isDirectory()) {
|
|
158
|
+
files = files.concat(getAllUserFiles(fullPath, baseDir));
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
files.push(path_1.default.relative(baseDir, fullPath));
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return files;
|
|
165
|
+
};
|
|
166
|
+
const userFiles = getAllUserFiles(userScriptsDir);
|
|
167
|
+
let cleanedCount = 0;
|
|
168
|
+
for (const userFile of userFiles) {
|
|
169
|
+
// Normalize path separators for comparison
|
|
170
|
+
const normalizedUserFile = userFile.replace(/\\/g, '/');
|
|
171
|
+
const normalizedRegistry = registryRelativePaths.map(p => p.replace(/\\/g, '/'));
|
|
172
|
+
if (!normalizedRegistry.includes(normalizedUserFile)) {
|
|
173
|
+
try {
|
|
174
|
+
const userFilePath = path_1.default.join(userScriptsDir, userFile);
|
|
175
|
+
fs_1.default.unlinkSync(userFilePath);
|
|
176
|
+
cleanedCount++;
|
|
177
|
+
console.log(chalk_1.default.yellow(` - ${userFile} (removed from registry)`));
|
|
178
|
+
// Cleanup empty directories
|
|
179
|
+
try {
|
|
180
|
+
const dir = path_1.default.dirname(userFilePath);
|
|
181
|
+
if (fs_1.default.existsSync(dir) && fs_1.default.readdirSync(dir).length === 0) {
|
|
182
|
+
fs_1.default.rmdirSync(dir);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
catch (e) { }
|
|
186
|
+
}
|
|
187
|
+
catch (error) {
|
|
188
|
+
console.warn(chalk_1.default.yellow(`⚠️ Could not remove obsolete script ${userFile}: ${error}`));
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
return cleanedCount;
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Get the path to a script in the user directory
|
|
196
|
+
*/
|
|
197
|
+
function getUserScriptPath(scriptName) {
|
|
198
|
+
return path_1.default.join(getUserScriptsDir(), scriptName);
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Check if a script exists in the user directory
|
|
202
|
+
*/
|
|
203
|
+
function userScriptExists(scriptName) {
|
|
204
|
+
return fs_1.default.existsSync(getUserScriptPath(scriptName));
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Validate that all expected scripts are present in user directory
|
|
208
|
+
*/
|
|
209
|
+
function validateUserScripts(expectedScripts) {
|
|
210
|
+
const missing = [];
|
|
211
|
+
const present = [];
|
|
212
|
+
for (const scriptName of expectedScripts) {
|
|
213
|
+
if (userScriptExists(scriptName)) {
|
|
214
|
+
present.push(scriptName);
|
|
215
|
+
}
|
|
216
|
+
else {
|
|
217
|
+
missing.push(scriptName);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
return { missing, present };
|
|
221
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
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.readUserFraimConfig = readUserFraimConfig;
|
|
7
|
+
exports.writeUserFraimConfig = writeUserFraimConfig;
|
|
8
|
+
exports.getInstalledIdes = getInstalledIdes;
|
|
9
|
+
exports.addInstalledIde = addInstalledIde;
|
|
10
|
+
exports.getOrganizationConfig = getOrganizationConfig;
|
|
11
|
+
exports.getManagerStorageConfig = getManagerStorageConfig;
|
|
12
|
+
/**
|
|
13
|
+
* Typed read/write helpers for the user-level FRAIM config (~/.fraim/config.json),
|
|
14
|
+
* including the organization block introduced by issue #563.
|
|
15
|
+
*
|
|
16
|
+
* The user-level config is machine-scoped and distinct from the workspace
|
|
17
|
+
* config (fraim/config.json). Honors the FRAIM_USER_DIR override used by tests.
|
|
18
|
+
*/
|
|
19
|
+
const fs_1 = __importDefault(require("fs"));
|
|
20
|
+
const path_1 = __importDefault(require("path"));
|
|
21
|
+
const project_fraim_paths_1 = require("../../core/utils/project-fraim-paths");
|
|
22
|
+
function getUserConfigPath() {
|
|
23
|
+
return path_1.default.join((0, project_fraim_paths_1.getUserFraimDirPath)(), 'config.json');
|
|
24
|
+
}
|
|
25
|
+
function readUserFraimConfig() {
|
|
26
|
+
try {
|
|
27
|
+
const configPath = getUserConfigPath();
|
|
28
|
+
if (!fs_1.default.existsSync(configPath))
|
|
29
|
+
return {};
|
|
30
|
+
const parsed = JSON.parse(fs_1.default.readFileSync(configPath, 'utf8'));
|
|
31
|
+
return parsed && typeof parsed === 'object' ? parsed : {};
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return {};
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Shallow-merge a patch into the user config and persist it. Existing keys
|
|
39
|
+
* not named in the patch are preserved. Setting a key to undefined removes it.
|
|
40
|
+
*/
|
|
41
|
+
function writeUserFraimConfig(patch) {
|
|
42
|
+
const current = readUserFraimConfig();
|
|
43
|
+
const merged = { ...current, ...patch };
|
|
44
|
+
for (const key of Object.keys(patch)) {
|
|
45
|
+
if (patch[key] === undefined)
|
|
46
|
+
delete merged[key];
|
|
47
|
+
}
|
|
48
|
+
const dir = (0, project_fraim_paths_1.getUserFraimDirPath)();
|
|
49
|
+
fs_1.default.mkdirSync(dir, { recursive: true });
|
|
50
|
+
fs_1.default.writeFileSync(getUserConfigPath(), JSON.stringify(merged, null, 2));
|
|
51
|
+
return merged;
|
|
52
|
+
}
|
|
53
|
+
function getInstalledIdes() {
|
|
54
|
+
const raw = readUserFraimConfig().installedIdes;
|
|
55
|
+
return Array.isArray(raw) ? raw : null;
|
|
56
|
+
}
|
|
57
|
+
function addInstalledIde(configType) {
|
|
58
|
+
const current = getInstalledIdes() ?? [];
|
|
59
|
+
if (!current.includes(configType)) {
|
|
60
|
+
writeUserFraimConfig({ installedIdes: [...current, configType] });
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Resolve the validated organization configuration, or null when no valid
|
|
65
|
+
* organization is configured on this machine (R1.1).
|
|
66
|
+
*/
|
|
67
|
+
function getOrganizationConfig() {
|
|
68
|
+
const raw = readUserFraimConfig().organization;
|
|
69
|
+
if (!raw || typeof raw !== 'object')
|
|
70
|
+
return null;
|
|
71
|
+
if (raw.backend === 'git') {
|
|
72
|
+
const gitUrl = typeof raw.gitUrl === 'string' ? raw.gitUrl.trim() : '';
|
|
73
|
+
if (!gitUrl)
|
|
74
|
+
return null;
|
|
75
|
+
return { backend: 'git', gitUrl, id: raw.id };
|
|
76
|
+
}
|
|
77
|
+
if (raw.backend === 'fraim-cloud') {
|
|
78
|
+
return { backend: 'fraim-cloud', id: raw.id };
|
|
79
|
+
}
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Resolve the validated manager storage configuration, or null when no portable
|
|
84
|
+
* manager backend is configured on this machine.
|
|
85
|
+
*/
|
|
86
|
+
function getManagerStorageConfig() {
|
|
87
|
+
const raw = readUserFraimConfig().managerStorage;
|
|
88
|
+
if (!raw || typeof raw !== 'object')
|
|
89
|
+
return null;
|
|
90
|
+
if (raw.backend === 'git') {
|
|
91
|
+
const gitUrl = typeof raw.gitUrl === 'string' ? raw.gitUrl.trim() : '';
|
|
92
|
+
if (!gitUrl)
|
|
93
|
+
return null;
|
|
94
|
+
return { backend: 'git', gitUrl, id: raw.id };
|
|
95
|
+
}
|
|
96
|
+
if (raw.backend === 'fraim-cloud') {
|
|
97
|
+
return { backend: 'fraim-cloud', id: raw.id };
|
|
98
|
+
}
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
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.getFraimVersion = getFraimVersion;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
function getFraimVersion() {
|
|
10
|
+
// Try reliable paths to find package.json relative to this file
|
|
11
|
+
// locally: src/cli/utils/version-utils.ts -> package.json is ../../../package.json
|
|
12
|
+
// dist: dist/src/cli/utils/version-utils.js -> package.json is ../../../../package.json
|
|
13
|
+
// Traverse up from __dirname until we find a FRAIM package.json.
|
|
14
|
+
// Fixed relative paths break when npx cache layouts differ across OS/npm versions.
|
|
15
|
+
let dir = __dirname;
|
|
16
|
+
for (let i = 0; i < 10; i++) {
|
|
17
|
+
const pkgPath = path_1.default.join(dir, 'package.json');
|
|
18
|
+
if (fs_1.default.existsSync(pkgPath)) {
|
|
19
|
+
try {
|
|
20
|
+
const pkg = JSON.parse(fs_1.default.readFileSync(pkgPath, 'utf-8'));
|
|
21
|
+
if (pkg.name === 'fraim' || pkg.name === 'fraim-hub' || pkg.name === 'fraim-workspace') {
|
|
22
|
+
return pkg.version;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
catch (e) {
|
|
26
|
+
// Ignore parsing errors
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
const parent = path_1.default.dirname(dir);
|
|
30
|
+
if (parent === dir)
|
|
31
|
+
break; // Reached filesystem root
|
|
32
|
+
dir = parent;
|
|
33
|
+
}
|
|
34
|
+
return 'unknown'; // Do not return a fake version — unknown is safer than misleading
|
|
35
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.HUMAN_MANAGER_PROFILES = exports.AI_MANAGER_QUALITIES = exports.HIRING_SERVICES = void 0;
|
|
4
|
+
exports.getHumanManagerProfile = getHumanManagerProfile;
|
|
5
|
+
exports.buildSearchQuery = buildSearchQuery;
|
|
6
|
+
exports.buildJobDescription = buildJobDescription;
|
|
7
|
+
exports.buildServiceUrl = buildServiceUrl;
|
|
8
|
+
exports.buildManagerHiringCatalog = buildManagerHiringCatalog;
|
|
9
|
+
/**
|
|
10
|
+
* Issue #538 — human-manager hiring engine (app side).
|
|
11
|
+
*
|
|
12
|
+
* Self-contained on purpose: the app (AI Hub UI modal + bootstrap) must resolve this
|
|
13
|
+
* at runtime in every packaging context (dev tsx, compiled dist, packed CLI), so it
|
|
14
|
+
* must NOT import across the app→registry boundary. The `hire-the-right-human-ai-manager`
|
|
15
|
+
* job uses the mirror engine `registry/scripts/ai-manager-hiring.ts` (self-contained +
|
|
16
|
+
* CLI). The two are kept identical by a parity test (tests/isolated/test-538-ai-manager-hiring.ts);
|
|
17
|
+
* that is the DRY contract — one behavior, enforced, with no fragile cross-boundary import.
|
|
18
|
+
*/
|
|
19
|
+
const persona_hiring_1 = require("./persona-hiring");
|
|
20
|
+
exports.HIRING_SERVICES = ['linkedin', 'indeed', 'seekout'];
|
|
21
|
+
/** The qualities of a strong AI manager — embedded in both the query and the JD. */
|
|
22
|
+
exports.AI_MANAGER_QUALITIES = [
|
|
23
|
+
{ title: 'Clear specification & written communication', detail: 'Can brief an AI agent so it executes correctly the first time.' },
|
|
24
|
+
{ title: 'Trust-but-verify judgment', detail: 'Knows when to delegate to the agent and when to review deeply.' },
|
|
25
|
+
{ title: 'Coaching mindset', detail: 'Gives feedback that compounds into reusable rules and learnings.' },
|
|
26
|
+
{ title: 'Outcome ownership & AI fluency', detail: 'Owns results; comfortable directing autonomous agents.' },
|
|
27
|
+
];
|
|
28
|
+
const GENERALIST_PROFILE = {
|
|
29
|
+
humanTitle: 'Manager',
|
|
30
|
+
keywords: ['"Manager"', '"Director"', '"Team Lead"', '"Chief of Staff"'],
|
|
31
|
+
};
|
|
32
|
+
/** Role key -> the human manager best suited to manage that AI employee. Mirror of registry/scripts/ai-manager-hiring.ts. */
|
|
33
|
+
exports.HUMAN_MANAGER_PROFILES = {
|
|
34
|
+
maestro: { humanTitle: 'Co-Founder / General Manager', keywords: ['"Co-Founder"', '"General Manager"', '"Chief of Staff"', '"Founder"'] },
|
|
35
|
+
beza: { humanTitle: 'Head of Strategy', keywords: ['"Head of Strategy"', '"Strategy Director"', '"Chief of Staff"'] },
|
|
36
|
+
pam: { humanTitle: 'Head of Product', keywords: ['"Head of Product"', '"Group Product Manager"', '"Director of Product"'] },
|
|
37
|
+
swen: { humanTitle: 'Engineering Manager', keywords: ['"Engineering Manager"', '"Tech Lead"', '"Staff Software Engineer"'] },
|
|
38
|
+
qasm: { humanTitle: 'QA Manager', keywords: ['"QA Manager"', '"QA Lead"', '"Head of Quality"'] },
|
|
39
|
+
huxley: { humanTitle: 'Design Manager', keywords: ['"Design Manager"', '"Head of Design"', '"Design Lead"'] },
|
|
40
|
+
gautam: { humanTitle: 'Marketing Director', keywords: ['"Head of Growth"', '"Marketing Director"', '"Demand Generation Lead"'] },
|
|
41
|
+
cela: { humanTitle: 'General Counsel', keywords: ['"General Counsel"', '"Head of Legal"', '"Legal Director"'] },
|
|
42
|
+
sekhar: { humanTitle: 'Security Manager', keywords: ['"Security Manager"', '"Head of Security"', '"CISO"'] },
|
|
43
|
+
ashley: { humanTitle: 'Chief of Staff', keywords: ['"Chief of Staff"', '"Executive Operations Manager"', '"Office Manager"'] },
|
|
44
|
+
mandy: { humanTitle: 'Head of Operations', keywords: ['"Head of Operations"', '"Director of Operations"', '"Program Manager"'] },
|
|
45
|
+
ricardo: { humanTitle: 'Recruiting Manager', keywords: ['"Recruiting Manager"', '"Head of Talent"', '"Talent Acquisition Lead"'] },
|
|
46
|
+
hari: { humanTitle: 'HR Manager', keywords: ['"HR Manager"', '"Head of People"', '"HR Business Partner"'] },
|
|
47
|
+
careena: { humanTitle: 'Head of Talent Development', keywords: ['"Head of Talent Development"', '"L&D Manager"', '"Career Coaching Lead"'] },
|
|
48
|
+
sade: { humanTitle: 'Salesforce Manager', keywords: ['"Salesforce Manager"', '"CRM Lead"', '"Salesforce Architect"'] },
|
|
49
|
+
sam: { humanTitle: 'Sales Manager', keywords: ['"Sales Manager"', '"Head of Sales"', '"Director of Sales"'] },
|
|
50
|
+
casey: { humanTitle: 'Customer Success Manager', keywords: ['"Customer Success Manager"', '"Head of Customer Success"', '"Support Manager"'] },
|
|
51
|
+
deidre: { humanTitle: 'Head of DEI', keywords: ['"Head of DEI"', '"Director of Inclusion"', '"DEI Program Manager"'] },
|
|
52
|
+
mona: { humanTitle: 'Finance Manager', keywords: ['"Finance Manager"', '"Head of Finance"', '"Controller"'] },
|
|
53
|
+
sreya: { humanTitle: 'SRE Manager', keywords: ['"SRE Manager"', '"Head of Reliability"', '"Platform Engineering Lead"'] },
|
|
54
|
+
procella: { humanTitle: 'Procurement Manager', keywords: ['"Procurement Manager"', '"Head of Procurement"', '"Sourcing Lead"'] },
|
|
55
|
+
banke: { humanTitle: 'KYC Operations Manager', keywords: ['"KYC Operations Manager"', '"AML Compliance Manager"', '"Banking Compliance Lead"'] },
|
|
56
|
+
auditya: { humanTitle: 'Audit Manager', keywords: ['"Audit Manager"', '"Internal Audit Lead"', '"Compliance Audit Manager"'] },
|
|
57
|
+
};
|
|
58
|
+
function getHumanManagerProfile(roleKey) {
|
|
59
|
+
return exports.HUMAN_MANAGER_PROFILES[roleKey] ?? GENERALIST_PROFILE;
|
|
60
|
+
}
|
|
61
|
+
const AI_MANAGER_QUERY_TERMS = '("AI" OR "LLM" OR "agentic") AND ("coaching" OR "mentoring")';
|
|
62
|
+
function buildSearchQuery(roleKey) {
|
|
63
|
+
const profile = getHumanManagerProfile(roleKey);
|
|
64
|
+
return `${profile.keywords.join(' OR ')} AND ${AI_MANAGER_QUERY_TERMS}`;
|
|
65
|
+
}
|
|
66
|
+
function buildJobDescription(roleKey) {
|
|
67
|
+
const profile = getHumanManagerProfile(roleKey);
|
|
68
|
+
const role = persona_hiring_1.PERSONA_HIRE_CATALOG[roleKey]?.role ?? 'AI Employee';
|
|
69
|
+
return [
|
|
70
|
+
`${profile.humanTitle} — manager for an ${role}`,
|
|
71
|
+
'',
|
|
72
|
+
`You will manage an ${role} (an autonomous AI agent) and the humans around it, owning the outcomes it ships.`,
|
|
73
|
+
'',
|
|
74
|
+
"What you'll do:",
|
|
75
|
+
`- Write crisp specifications and acceptance criteria the ${role} can execute against.`,
|
|
76
|
+
`- Review the agent's output with judgment: know when to trust it and when to dig in.`,
|
|
77
|
+
'- Coach the agent and team so feedback compounds into reusable rules.',
|
|
78
|
+
'- Own delivery outcomes, not activity; prioritize ruthlessly.',
|
|
79
|
+
'',
|
|
80
|
+
'What makes a strong AI manager:',
|
|
81
|
+
...exports.AI_MANAGER_QUALITIES.map((q) => `- ${q.title}: ${q.detail}`),
|
|
82
|
+
].join('\n');
|
|
83
|
+
}
|
|
84
|
+
function buildServiceUrl(service, query, location) {
|
|
85
|
+
const q = encodeURIComponent(query);
|
|
86
|
+
const loc = location && location.trim().length > 0 ? location.trim() : '';
|
|
87
|
+
switch (service) {
|
|
88
|
+
case 'linkedin':
|
|
89
|
+
return `https://www.linkedin.com/search/results/people/?keywords=${q}`;
|
|
90
|
+
case 'indeed':
|
|
91
|
+
return `https://www.indeed.com/jobs?q=${q}${loc ? `&l=${encodeURIComponent(loc)}` : ''}`;
|
|
92
|
+
case 'seekout':
|
|
93
|
+
return 'https://app.seekout.com/';
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Catalog projection for the AI Hub bootstrap so the client renders off the server's
|
|
98
|
+
* source of truth. Defaults to exactly the hireable personas in `PERSONA_HIRE_CATALOG`.
|
|
99
|
+
*/
|
|
100
|
+
function buildManagerHiringCatalog(roleKeys = Object.keys(persona_hiring_1.PERSONA_HIRE_CATALOG)) {
|
|
101
|
+
const roles = {};
|
|
102
|
+
for (const key of roleKeys) {
|
|
103
|
+
const profile = getHumanManagerProfile(key);
|
|
104
|
+
const query = buildSearchQuery(key);
|
|
105
|
+
// managedRole is derived from PERSONA_HIRE_CATALOG so the bootstrap response
|
|
106
|
+
// shape is unchanged even after removing the field from HumanManagerProfile.
|
|
107
|
+
const managedRole = persona_hiring_1.PERSONA_HIRE_CATALOG[key]?.role ?? 'AI Employee';
|
|
108
|
+
roles[key] = {
|
|
109
|
+
...profile,
|
|
110
|
+
managedRole,
|
|
111
|
+
query,
|
|
112
|
+
jobDescription: buildJobDescription(key),
|
|
113
|
+
serviceUrls: {
|
|
114
|
+
linkedin: buildServiceUrl('linkedin', query),
|
|
115
|
+
indeed: buildServiceUrl('indeed', query),
|
|
116
|
+
seekout: buildServiceUrl('seekout', query),
|
|
117
|
+
},
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
return { qualities: exports.AI_MANAGER_QUALITIES, services: exports.HIRING_SERVICES, roles };
|
|
121
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getFeatureFlags = getFeatureFlags;
|
|
4
|
+
exports.isPersonaEntitlementsEnabled = isPersonaEntitlementsEnabled;
|
|
5
|
+
exports.getPublicFeatureFlags = getPublicFeatureFlags;
|
|
6
|
+
function parseBooleanFlag(value) {
|
|
7
|
+
if (!value)
|
|
8
|
+
return false;
|
|
9
|
+
return ['1', 'true', 'yes', 'on'].includes(value.trim().toLowerCase());
|
|
10
|
+
}
|
|
11
|
+
function getFeatureFlags() {
|
|
12
|
+
return {
|
|
13
|
+
personaEntitlements: {
|
|
14
|
+
enabled: parseBooleanFlag(process.env.FRAIM_FF_PERSONA_ENTITLEMENTS)
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
function isPersonaEntitlementsEnabled() {
|
|
19
|
+
return getFeatureFlags().personaEntitlements.enabled;
|
|
20
|
+
}
|
|
21
|
+
function getPublicFeatureFlags() {
|
|
22
|
+
return {
|
|
23
|
+
personaEntitlementsEnabled: isPersonaEntitlementsEnabled()
|
|
24
|
+
};
|
|
25
|
+
}
|