fraim 2.0.203 → 2.0.205

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/README.md +3 -3
  2. package/bin/fraim.js +8 -18
  3. package/dist/src/cli/commands/first-run.js +2 -37
  4. package/dist/src/cli/doctor/checks/mcp-connectivity-checks.js +1 -1
  5. package/dist/src/cli/fraim.js +0 -2
  6. package/dist/src/cli/setup/ide-invocation-surfaces.js +6 -6
  7. package/dist/src/cli/utils/managed-agent-paths.js +76 -6
  8. package/dist/src/cli/utils/version-utils.js +2 -2
  9. package/dist/src/config/persona-capability-bundles.js +3 -3
  10. package/dist/src/core/job-phases.js +168 -0
  11. package/dist/src/core/utils/ports.js +32 -0
  12. package/dist/src/core/utils/stub-generator.js +24 -2
  13. package/dist/src/first-run/session-service.js +106 -24
  14. package/dist/src/local-mcp-server/learning-context-builder.js +1 -1
  15. package/dist/src/local-mcp-server/stdio-server.js +41 -9
  16. package/dist/src/routes/analytics.js +2 -2
  17. package/dist/src/services/registry-service.js +2 -2
  18. package/index.js +1 -1
  19. package/package.json +61 -181
  20. package/dist/src/ai-hub/catalog.js +0 -406
  21. package/dist/src/ai-hub/cert-store.js +0 -70
  22. package/dist/src/ai-hub/conversation-store.js +0 -359
  23. package/dist/src/ai-hub/desktop-main.js +0 -449
  24. package/dist/src/ai-hub/hosts.js +0 -1664
  25. package/dist/src/ai-hub/hub-latest-version.js +0 -52
  26. package/dist/src/ai-hub/hub-launch-decision.js +0 -57
  27. package/dist/src/ai-hub/hub-runtime-file.js +0 -43
  28. package/dist/src/ai-hub/managed-browser.js +0 -269
  29. package/dist/src/ai-hub/manager-turns.js +0 -64
  30. package/dist/src/ai-hub/office-sideload.js +0 -156
  31. package/dist/src/ai-hub/openclaw-bridge.js +0 -250
  32. package/dist/src/ai-hub/preferences.js +0 -201
  33. package/dist/src/ai-hub/remote-hub-gateway.js +0 -88
  34. package/dist/src/ai-hub/server.js +0 -3827
  35. package/dist/src/ai-hub/types.js +0 -2
  36. package/dist/src/ai-hub/word-sideload.js +0 -95
  37. package/dist/src/cli/commands/hub.js +0 -238
  38. package/dist/src/cli/commands/test-mcp.js +0 -171
  39. package/dist/src/cli/setup/first-run.js +0 -242
  40. package/dist/src/core/config-writer.js +0 -75
  41. package/dist/src/core/utils/job-aliases.js +0 -47
  42. package/dist/src/core/utils/workflow-parser.js +0 -174
  43. package/dist/src/services/email-service-clean.js +0 -782
  44. package/extensions/office-word/favicon.ico +0 -0
  45. package/extensions/office-word/icon-64.png +0 -0
  46. package/extensions/office-word/manifest.xml +0 -33
  47. package/extensions/office-word/taskpane.html +0 -242
  48. package/public/ai-hub/index.html +0 -894
  49. package/public/ai-hub/powerpoint-taskpane/icon-64.png +0 -0
  50. package/public/ai-hub/powerpoint-taskpane/index.html +0 -236
  51. package/public/ai-hub/powerpoint-taskpane/manifest.xml +0 -30
  52. package/public/ai-hub/review.css +0 -381
  53. package/public/ai-hub/script.js +0 -11015
  54. package/public/ai-hub/styles.css +0 -4873
  55. package/public/portfolio/ashley.html +0 -523
  56. package/public/portfolio/auditya.html +0 -83
  57. package/public/portfolio/banke.html +0 -83
  58. package/public/portfolio/beza.html +0 -659
  59. package/public/portfolio/careena.html +0 -632
  60. package/public/portfolio/casey.html +0 -568
  61. package/public/portfolio/celia.html +0 -490
  62. package/public/portfolio/deidre.html +0 -642
  63. package/public/portfolio/gautam.html +0 -597
  64. package/public/portfolio/hari.html +0 -469
  65. package/public/portfolio/huxley.html +0 -1354
  66. package/public/portfolio/index.html +0 -741
  67. package/public/portfolio/maestro.html +0 -518
  68. package/public/portfolio/mandy.html +0 -590
  69. package/public/portfolio/mona.html +0 -597
  70. package/public/portfolio/pam.html +0 -887
  71. package/public/portfolio/procella.html +0 -107
  72. package/public/portfolio/qasm.html +0 -569
  73. package/public/portfolio/ricardo.html +0 -489
  74. package/public/portfolio/sade.html +0 -560
  75. package/public/portfolio/sam.html +0 -654
  76. package/public/portfolio/sechar.html +0 -580
  77. package/public/portfolio/sreya.html +0 -599
  78. package/public/portfolio/swen.html +0 -601
  79. /package/dist/src/{ai-hub → core}/brand-store.js +0 -0
@@ -1,250 +0,0 @@
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.OpenClawAiHubBridge = void 0;
7
- const express_1 = __importDefault(require("express"));
8
- const fs_1 = __importDefault(require("fs"));
9
- const path_1 = __importDefault(require("path"));
10
- const axios_1 = __importDefault(require("axios"));
11
- function lastMessage(messages, role) {
12
- return [...messages].reverse().find((message) => message.role === role);
13
- }
14
- function buildStatusLine(run) {
15
- const status = run.status.toUpperCase();
16
- const phase = run.currentPhase ? ` | phase: ${run.currentPhase}` : '';
17
- return `${status} | ${run.jobId} | ${run.hostId}${phase}`;
18
- }
19
- function buildStagesLine(run) {
20
- if (!run.stages?.length)
21
- return null;
22
- const line = run.stages
23
- .map((stage) => {
24
- const marker = stage.state === 'done' ? '[done]' : stage.state === 'current' ? '[now]' : '[todo]';
25
- return `${marker} ${stage.label}`;
26
- })
27
- .join(' | ');
28
- return line || null;
29
- }
30
- function buildBridgeReply(run) {
31
- const replyParts = [buildStatusLine(run)];
32
- const employee = lastMessage(run.messages, 'employee');
33
- if (employee?.text)
34
- replyParts.push(employee.text);
35
- const stages = buildStagesLine(run);
36
- if (stages)
37
- replyParts.push(`Stages: ${stages}`);
38
- return replyParts.join('\n\n');
39
- }
40
- class OpenClawAiHubBridge {
41
- constructor(options) {
42
- this.app = (0, express_1.default)();
43
- this.threads = new Map();
44
- this.api = axios_1.default.create({
45
- baseURL: options.hubBaseUrl.replace(/\/$/, ''),
46
- timeout: 5000,
47
- validateStatus: () => true,
48
- });
49
- this.defaultProjectPath = options.defaultProjectPath;
50
- this.defaultHostId = options.defaultHostId || 'codex';
51
- this.defaultJobId = options.defaultJobId;
52
- this.pollingIntervalMs = options.pollingIntervalMs ?? 40;
53
- this.pollingTimeoutMs = options.pollingTimeoutMs ?? 4000;
54
- this.threadStatePath = options.threadStatePath ? path_1.default.resolve(options.threadStatePath) : undefined;
55
- this.missingJobReplyText = options.missingJobReplyText || 'Send /fraim <job-id> <request> to start a FRAIM thread.';
56
- this.loadThreadState();
57
- this.app.use(express_1.default.json());
58
- this.registerRoutes();
59
- }
60
- async start(port) {
61
- if (this.httpServer)
62
- return;
63
- await new Promise((resolve) => {
64
- this.httpServer = this.app.listen(port, '127.0.0.1', () => resolve());
65
- });
66
- }
67
- async stop() {
68
- if (!this.httpServer)
69
- return;
70
- await new Promise((resolve, reject) => {
71
- this.httpServer?.close((error) => (error ? reject(error) : resolve()));
72
- });
73
- this.httpServer = undefined;
74
- }
75
- registerRoutes() {
76
- this.app.get('/api/openclaw-bridge/health', (_req, res) => {
77
- res.json({ status: 'ok', service: 'openclaw-ai-hub-bridge', threadCount: this.threads.size });
78
- });
79
- this.app.get('/api/openclaw-bridge/threads', async (req, res) => {
80
- try {
81
- const channelId = String(req.query.channelId || 'telegram').trim();
82
- const threadId = String(req.query.threadId || '').trim();
83
- if (!threadId)
84
- return res.status(400).json({ error: 'threadId is required.' });
85
- const state = this.threads.get(this.threadKey(channelId, threadId));
86
- if (!state)
87
- return res.status(404).json({ error: 'Thread not found.' });
88
- const run = await this.fetchRun(state.runId);
89
- return res.json(this.presentThread(state, run));
90
- }
91
- catch (error) {
92
- return res.status(500).json({ error: error instanceof Error ? error.message : 'Could not load thread.' });
93
- }
94
- });
95
- this.app.post('/api/openclaw-bridge/messages', async (req, res) => {
96
- try {
97
- const inbound = this.normalizeInbound(req.body);
98
- if (!inbound.threadId)
99
- return res.status(400).json({ error: 'threadId is required.' });
100
- if (!inbound.text)
101
- return res.status(400).json({ error: 'text is required.' });
102
- const key = this.threadKey(inbound.channelId, inbound.threadId);
103
- let state = this.threads.get(key);
104
- let run;
105
- const startedNewRun = !state;
106
- if (!state) {
107
- const hostId = inbound.hostId || this.defaultHostId;
108
- const projectPath = inbound.projectPath || this.defaultProjectPath;
109
- const jobId = inbound.jobId || this.defaultJobId;
110
- const start = await this.api.post('/api/ai-hub/runs', {
111
- projectPath,
112
- hostId,
113
- ...(jobId ? { jobId } : {}),
114
- instructions: inbound.text,
115
- });
116
- if (start.status !== 201) {
117
- const errorText = typeof start.data?.error === 'string' ? start.data.error : '';
118
- if (!jobId && /choose a fraim job/i.test(errorText)) {
119
- return res.json({
120
- ok: true,
121
- startedNewRun: false,
122
- thread: null,
123
- outboundMessages: [{ text: this.missingJobReplyText }],
124
- });
125
- }
126
- return res.status(start.status || 400).json(start.data);
127
- }
128
- run = start.data;
129
- state = {
130
- channelId: inbound.channelId,
131
- threadId: inbound.threadId,
132
- runId: run.id,
133
- hostId,
134
- jobId: run.jobId,
135
- projectPath,
136
- lastSeenAt: new Date().toISOString(),
137
- managerTexts: [inbound.text],
138
- };
139
- this.threads.set(key, state);
140
- this.persistThreadState();
141
- }
142
- else {
143
- const continued = await this.api.post(`/api/ai-hub/runs/${state.runId}/messages`, {
144
- instructions: inbound.text,
145
- });
146
- if (continued.status !== 200) {
147
- return res.status(continued.status || 400).json(continued.data);
148
- }
149
- run = continued.data;
150
- state.lastSeenAt = new Date().toISOString();
151
- state.managerTexts = [...(state.managerTexts ?? []), inbound.text];
152
- this.persistThreadState();
153
- }
154
- const settled = await this.waitForSettlement(run.id);
155
- return res.json({
156
- ok: true,
157
- startedNewRun,
158
- thread: this.presentThread(state, settled),
159
- outboundMessages: [{ text: buildBridgeReply(settled) }],
160
- });
161
- }
162
- catch (error) {
163
- return res.status(500).json({ error: error instanceof Error ? error.message : 'Could not process message.' });
164
- }
165
- });
166
- }
167
- normalizeInbound(body) {
168
- const context = body.context || {};
169
- const metadata = context.metadata || {};
170
- const metaThreadId = typeof metadata.threadId === 'string' ? metadata.threadId : null;
171
- const metaChatId = typeof metadata.chatId === 'string' ? metadata.chatId : null;
172
- const metaSenderId = typeof metadata.senderId === 'string' ? metadata.senderId : null;
173
- return {
174
- ...body,
175
- channelId: String(body.channelId || context.channelId || 'telegram').trim(),
176
- threadId: String(body.threadId || context.threadId || metaThreadId || body.chatId || body.senderId || metaChatId || metaSenderId || '').trim(),
177
- text: String(body.text || context.content || '').trim(),
178
- };
179
- }
180
- async fetchRun(runId) {
181
- const response = await this.api.get(`/api/ai-hub/runs/${runId}`);
182
- if (response.status !== 200) {
183
- throw new Error(`AI Hub returned ${response.status} while loading run ${runId}.`);
184
- }
185
- return response.data;
186
- }
187
- async waitForSettlement(runId) {
188
- const startedAt = Date.now();
189
- while (Date.now() - startedAt < this.pollingTimeoutMs) {
190
- const run = await this.fetchRun(runId);
191
- if (run.status !== 'running')
192
- return run;
193
- await new Promise((resolve) => setTimeout(resolve, this.pollingIntervalMs));
194
- }
195
- return this.fetchRun(runId);
196
- }
197
- presentThread(state, run) {
198
- // The Hub records the invocation it sent as each manager bubble (#696). For a channel
199
- // transcript we substitute the user's own words (state.managerTexts, in turn order)
200
- // for manager-role messages, so the thread reflects what the user actually typed.
201
- const managerTexts = state.managerTexts ?? [];
202
- let managerIdx = 0;
203
- const transcript = run.messages.map((message) => ({
204
- role: message.role,
205
- text: message.role === 'manager' && managerIdx < managerTexts.length
206
- ? managerTexts[managerIdx++]
207
- : message.text,
208
- createdAt: message.createdAt,
209
- }));
210
- const latestManagerMessage = [...transcript].reverse().find((m) => m.role === 'manager')?.text || null;
211
- return {
212
- channelId: state.channelId,
213
- threadId: state.threadId,
214
- runId: state.runId,
215
- hostId: state.hostId,
216
- jobId: state.jobId,
217
- projectPath: state.projectPath,
218
- status: run.status,
219
- currentPhase: run.currentPhase || null,
220
- stages: run.stages || [],
221
- totals: run.totals || null,
222
- latestManagerMessage,
223
- latestEmployeeMessage: lastMessage(run.messages, 'employee')?.text || null,
224
- transcript,
225
- };
226
- }
227
- threadKey(channelId, threadId) {
228
- return `${channelId}:${threadId}`;
229
- }
230
- loadThreadState() {
231
- if (!this.threadStatePath || !fs_1.default.existsSync(this.threadStatePath))
232
- return;
233
- try {
234
- const raw = JSON.parse(fs_1.default.readFileSync(this.threadStatePath, 'utf8'));
235
- for (const thread of raw.threads || []) {
236
- this.threads.set(this.threadKey(thread.channelId, thread.threadId), thread);
237
- }
238
- }
239
- catch {
240
- // Ignore corrupt or partial state and start fresh.
241
- }
242
- }
243
- persistThreadState() {
244
- if (!this.threadStatePath)
245
- return;
246
- fs_1.default.mkdirSync(path_1.default.dirname(this.threadStatePath), { recursive: true });
247
- fs_1.default.writeFileSync(this.threadStatePath, JSON.stringify({ threads: [...this.threads.values()] }, null, 2), 'utf8');
248
- }
249
- }
250
- exports.OpenClawAiHubBridge = OpenClawAiHubBridge;
@@ -1,201 +0,0 @@
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.AiHubPreferencesStore = void 0;
7
- exports.normalizeAiHubProjectList = normalizeAiHubProjectList;
8
- const fs_1 = __importDefault(require("fs"));
9
- const crypto_1 = require("crypto");
10
- const path_1 = __importDefault(require("path"));
11
- const project_fraim_paths_1 = require("../core/utils/project-fraim-paths");
12
- const DEFAULT_CATEGORY = 'marketing';
13
- const DEFAULT_EMPLOYEE = 'claude';
14
- const defaultPreferences = (projectPath) => ({
15
- projectPath,
16
- employeeId: DEFAULT_EMPLOYEE,
17
- categoryId: DEFAULT_CATEGORY,
18
- recentJobIds: [],
19
- recentJobInstructions: {},
20
- personaKey: null,
21
- projects: normalizeAiHubProjectList([], projectPath),
22
- removedProjectPaths: [],
23
- });
24
- function normalizeProjectPath(projectPath) {
25
- return path_1.default.resolve(projectPath || process.cwd());
26
- }
27
- function canonicalProjectPath(projectPath) {
28
- const normalized = normalizeProjectPath(projectPath);
29
- return process.platform === 'win32' ? normalized.toLowerCase() : normalized;
30
- }
31
- function projectPathExists(projectPath) {
32
- try {
33
- return fs_1.default.statSync(projectPath).isDirectory();
34
- }
35
- catch {
36
- return false;
37
- }
38
- }
39
- function projectIdForPath(projectPath) {
40
- return `p-${(0, crypto_1.createHash)('sha1').update(canonicalProjectPath(projectPath)).digest('base64url').slice(0, 16)}`;
41
- }
42
- function normalizeRemovedProjectPaths(raw, currentProjectPath) {
43
- if (!Array.isArray(raw))
44
- return [];
45
- const currentKey = currentProjectPath ? canonicalProjectPath(currentProjectPath) : null;
46
- const seen = new Set();
47
- for (const value of raw) {
48
- if (typeof value !== 'string' || value.trim().length === 0)
49
- continue;
50
- const key = canonicalProjectPath(value);
51
- if (currentKey && key === currentKey)
52
- continue;
53
- seen.add(key);
54
- }
55
- return Array.from(seen);
56
- }
57
- function uniqueProjectId(entry, seenIds) {
58
- const fallbackId = projectIdForPath(entry.folderPath);
59
- const rawId = typeof entry.id === 'string' ? entry.id.trim() : '';
60
- const preferredId = rawId && rawId !== 'p-current' ? rawId : fallbackId;
61
- let id = preferredId;
62
- let suffix = 2;
63
- while (seenIds.has(id)) {
64
- id = `${fallbackId}-${suffix}`;
65
- suffix += 1;
66
- }
67
- seenIds.add(id);
68
- return id;
69
- }
70
- function withUniqueProjectIds(projects) {
71
- const seenIds = new Set();
72
- return projects.map((entry) => ({ ...entry, id: uniqueProjectId(entry, seenIds) }));
73
- }
74
- function normalizeProjectEntry(raw, fallbackPath) {
75
- if (!raw || typeof raw !== 'object')
76
- return null;
77
- const value = raw;
78
- const rawPath = typeof value.folderPath === 'string'
79
- ? value.folderPath
80
- : (typeof value.path === 'string'
81
- ? value.path
82
- : (typeof value.folder === 'string' ? value.folder : fallbackPath));
83
- if (!rawPath || typeof rawPath !== 'string')
84
- return null;
85
- const folderPath = normalizeProjectPath(rawPath);
86
- const basename = path_1.default.basename(folderPath) || folderPath;
87
- const rawId = typeof value.id === 'string' ? value.id.trim() : '';
88
- // The employee roster is deliberately dropped here (not read, not persisted):
89
- // it is derived from the bootstrap's hired personas at render time so it stays
90
- // consistent across machines. Any `team` on incoming/legacy state is discarded.
91
- return {
92
- id: rawId || projectIdForPath(folderPath),
93
- name: typeof value.name === 'string' && value.name.length > 0 ? value.name : basename,
94
- folderPath,
95
- intent: typeof value.intent === 'string' ? value.intent : '',
96
- outcome: typeof value.outcome === 'string' ? value.outcome : '',
97
- rules: typeof value.rules === 'string' ? value.rules : '',
98
- brief: typeof value.brief === 'string' ? value.brief : (typeof value.intent === 'string' ? value.intent : ''),
99
- updatedAt: typeof value.updatedAt === 'string' ? value.updatedAt : undefined,
100
- };
101
- }
102
- function normalizeAiHubProjectList(projects, currentProjectPath, options = {}) {
103
- const byPath = new Map();
104
- const currentKey = currentProjectPath ? normalizeProjectPath(currentProjectPath) : null;
105
- const removedKeys = new Set(normalizeRemovedProjectPaths(options.removedProjectPaths, currentProjectPath));
106
- const add = (entry) => {
107
- if (!entry)
108
- return;
109
- const key = normalizeProjectPath(entry.folderPath);
110
- if (removedKeys.has(canonicalProjectPath(key)))
111
- return;
112
- if (!options.includeMissing && key !== currentKey && !projectPathExists(key))
113
- return;
114
- const existing = byPath.get(key);
115
- byPath.set(key, existing ? { ...existing, ...entry, folderPath: key } : { ...entry, folderPath: key });
116
- };
117
- if (currentProjectPath)
118
- add(normalizeProjectEntry({ folderPath: currentProjectPath }, currentProjectPath));
119
- for (const project of projects)
120
- add(normalizeProjectEntry(project));
121
- return withUniqueProjectIds(Array.from(byPath.values()));
122
- }
123
- class AiHubPreferencesStore {
124
- constructor(stateFilePath = path_1.default.join((0, project_fraim_paths_1.getUserFraimDirPath)(), 'ai-hub-state.json')) {
125
- this.stateFilePath = stateFilePath;
126
- }
127
- load(projectPath) {
128
- if (!fs_1.default.existsSync(this.stateFilePath)) {
129
- return defaultPreferences(projectPath);
130
- }
131
- try {
132
- const raw = JSON.parse(fs_1.default.readFileSync(this.stateFilePath, 'utf8'));
133
- const removedProjectPaths = normalizeRemovedProjectPaths(raw.removedProjectPaths, projectPath);
134
- return {
135
- projectPath: raw.projectPath || projectPath,
136
- employeeId: (raw.employeeId === 'claude' || raw.employeeId === 'codex' || raw.employeeId === 'gemini' || raw.employeeId === 'copilot') ? raw.employeeId : DEFAULT_EMPLOYEE,
137
- categoryId: typeof raw.categoryId === 'string' && raw.categoryId.length > 0 ? raw.categoryId : DEFAULT_CATEGORY,
138
- recentJobIds: Array.isArray(raw.recentJobIds) ? raw.recentJobIds.filter((value) => typeof value === 'string') : [],
139
- recentJobInstructions: (typeof raw.recentJobInstructions === 'object' && raw.recentJobInstructions !== null && !Array.isArray(raw.recentJobInstructions))
140
- ? raw.recentJobInstructions
141
- : {},
142
- personaKey: typeof raw.personaKey === 'string' ? raw.personaKey : null,
143
- // Issue #750: apiKey is no longer a persisted field here at all — any
144
- // legacy apiKey left over from a pre-#750 ai-hub-state.json is silently
145
- // dropped, never read back. Identity comes from ~/.fraim/config.json only.
146
- projects: normalizeAiHubProjectList(Array.isArray(raw.projects) ? raw.projects : [], projectPath, { removedProjectPaths }),
147
- removedProjectPaths,
148
- };
149
- }
150
- catch {
151
- return defaultPreferences(projectPath);
152
- }
153
- }
154
- save(preferences) {
155
- fs_1.default.mkdirSync(path_1.default.dirname(this.stateFilePath), { recursive: true });
156
- fs_1.default.writeFileSync(this.stateFilePath, JSON.stringify(preferences, null, 2));
157
- }
158
- saveProjects(projectPath, projects, options = {}) {
159
- const normalizedProjectPath = normalizeProjectPath(projectPath);
160
- const preferences = this.load(normalizedProjectPath);
161
- const reviveKeys = new Set(normalizeRemovedProjectPaths(options.reviveRemovedPaths, normalizedProjectPath));
162
- const removedProjectPaths = normalizeRemovedProjectPaths(preferences.removedProjectPaths, normalizedProjectPath)
163
- .filter((removedPath) => !reviveKeys.has(removedPath));
164
- const nextProjects = normalizeAiHubProjectList(projects, normalizedProjectPath, { removedProjectPaths });
165
- this.save({ ...preferences, projectPath: normalizedProjectPath, projects: nextProjects, removedProjectPaths });
166
- return nextProjects;
167
- }
168
- mergeProjects(projectPath, projects) {
169
- const normalizedProjectPath = normalizeProjectPath(projectPath);
170
- const preferences = this.load(normalizedProjectPath);
171
- const removedProjectPaths = normalizeRemovedProjectPaths(preferences.removedProjectPaths, normalizedProjectPath);
172
- const nextProjects = normalizeAiHubProjectList([...(preferences.projects || []), ...projects], normalizedProjectPath, { removedProjectPaths });
173
- this.save({ ...preferences, projectPath: normalizedProjectPath, projects: nextProjects, removedProjectPaths });
174
- return nextProjects;
175
- }
176
- removeProject(projectPath, projects, removedProjectPath) {
177
- const normalizedProjectPath = normalizeProjectPath(projectPath);
178
- const preferences = this.load(normalizedProjectPath);
179
- const removedProjectPaths = normalizeRemovedProjectPaths([...(preferences.removedProjectPaths || []), removedProjectPath], normalizedProjectPath);
180
- const nextProjects = normalizeAiHubProjectList(projects, normalizedProjectPath, { removedProjectPaths });
181
- this.save({ ...preferences, projectPath: normalizedProjectPath, projects: nextProjects, removedProjectPaths });
182
- return nextProjects;
183
- }
184
- remember(preferences, jobId, instructions) {
185
- const recentJobIds = jobId
186
- ? [jobId, ...preferences.recentJobIds.filter((value) => value !== jobId)].slice(0, 8)
187
- : preferences.recentJobIds.slice(0, 8);
188
- const recentJobInstructions = { ...preferences.recentJobInstructions };
189
- if (jobId && instructions) {
190
- recentJobInstructions[jobId] = instructions.slice(0, 500);
191
- }
192
- const next = {
193
- ...preferences,
194
- recentJobIds,
195
- recentJobInstructions,
196
- };
197
- this.save(next);
198
- return next;
199
- }
200
- }
201
- exports.AiHubPreferencesStore = AiHubPreferencesStore;
@@ -1,88 +0,0 @@
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.HttpHubRemoteGateway = void 0;
7
- exports.resolveFraimRemoteUrl = resolveFraimRemoteUrl;
8
- const axios_1 = __importDefault(require("axios"));
9
- function resolveFraimRemoteUrl(explicit) {
10
- return (explicit || process.env.FRAIM_REMOTE_URL || 'https://fraim.wellnessatwork.me').replace(/\/+$/, '');
11
- }
12
- /**
13
- * Default HTTP-backed gateway. Mirrors the ProviderClient axios pattern:
14
- * base URL from FRAIM_REMOTE_URL, `x-api-key` auth header, 10s timeout.
15
- */
16
- class HttpHubRemoteGateway {
17
- constructor(serverUrl) {
18
- this.baseURL = resolveFraimRemoteUrl(serverUrl);
19
- }
20
- client(apiKey) {
21
- return axios_1.default.create({
22
- baseURL: this.baseURL,
23
- headers: { 'x-api-key': apiKey, 'Content-Type': 'application/json' },
24
- timeout: 10000,
25
- });
26
- }
27
- async getPersonaState(apiKey) {
28
- if (!apiKey)
29
- return null;
30
- try {
31
- const res = await this.client(apiKey).get('/api/personas/me');
32
- return res.data;
33
- }
34
- catch (err) {
35
- // 401 (no/expired key) or 404 (feature disabled) => treat as "no state" so the Hub
36
- // renders a clean locked/not-signed-in view instead of crashing.
37
- const status = err?.response?.status;
38
- if (status === 401 || status === 404)
39
- return null;
40
- console.warn('[ai-hub] getPersonaState failed:', err?.message || err);
41
- return null;
42
- }
43
- }
44
- async listManagerTeam(apiKey) {
45
- if (!apiKey)
46
- return [];
47
- try {
48
- const res = await this.client(apiKey).get('/api/ai-hub/manager-team');
49
- const team = (res.data?.team ?? []);
50
- return Array.isArray(team) ? team : [];
51
- }
52
- catch (err) {
53
- const status = err?.response?.status;
54
- if (status === 401 || status === 404)
55
- return [];
56
- console.warn('[ai-hub] listManagerTeam failed:', err?.message || err);
57
- return [];
58
- }
59
- }
60
- async assignManagerTeam(apiKey, personaKey) {
61
- if (!apiKey)
62
- return { status: 401, body: { error: 'authentication_required' } };
63
- try {
64
- const res = await this.client(apiKey).post('/api/ai-hub/manager-team/assign', { personaKey });
65
- return { status: res.status, body: res.data };
66
- }
67
- catch (err) {
68
- if (err?.response)
69
- return { status: err.response.status, body: err.response.data };
70
- console.warn('[ai-hub] assignManagerTeam failed:', err?.message || err);
71
- return { status: 500, body: { error: 'internal_error' } };
72
- }
73
- }
74
- async removeManagerTeam(apiKey, personaKey) {
75
- if (!apiKey)
76
- return;
77
- try {
78
- await this.client(apiKey).delete(`/api/ai-hub/manager-team/assign/${encodeURIComponent(personaKey)}`);
79
- }
80
- catch (err) {
81
- const status = err?.response?.status;
82
- if (status === 401 || status === 404)
83
- return;
84
- console.warn('[ai-hub] removeManagerTeam failed:', err?.message || err);
85
- }
86
- }
87
- }
88
- exports.HttpHubRemoteGateway = HttpHubRemoteGateway;