fraim-framework 2.0.183 → 2.0.185

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.
@@ -21,15 +21,6 @@ let isQuitting = false; // distinguishes window-close (→ tray) from app-quit
21
21
  // ---------------------------------------------------------------------------
22
22
  // Helpers
23
23
  // ---------------------------------------------------------------------------
24
- function tryCreateDbService() {
25
- try {
26
- const loaded = require('../fraim/db-service');
27
- return new loaded.FraimDbService();
28
- }
29
- catch {
30
- return undefined;
31
- }
32
- }
33
24
  function preferredWindowSize() {
34
25
  const { workAreaSize } = electron_1.screen.getPrimaryDisplay();
35
26
  return {
@@ -255,7 +246,8 @@ async function launchDesktopShell(options) {
255
246
  const certBundle = await (0, cert_store_1.loadOrCreateCert)();
256
247
  server = new server_1.AiHubServer({
257
248
  projectPath: options.projectPath,
258
- dbService: tryCreateDbService(),
249
+ // Issue #701: no local DB. Persona/manager-team state resolves through the hosted
250
+ // server via the Hub's default remote gateway (authenticated by the user's API key).
259
251
  httpsPort,
260
252
  certBundle,
261
253
  folderPicker: async () => {
@@ -592,7 +592,7 @@ function parseFraimInvocation(message) {
592
592
  const trimmed = message.trim();
593
593
  if (!trimmed)
594
594
  return null;
595
- const match = trimmed.match(/^[/$]fraim(?:\s+(\S+))?\s*([\s\S]*)$/);
595
+ const match = trimmed.match(/^[$/@]fraim(?:\s+(\S+))?\s*([\s\S]*)$/);
596
596
  if (!match)
597
597
  return null;
598
598
  return {
@@ -6,14 +6,14 @@ exports.buildCommunicationStyleNote = buildCommunicationStyleNote;
6
6
  exports.buildManagerMessage = buildManagerMessage;
7
7
  function extractExplicitFraimInvocation(text) {
8
8
  const raw = String(text || '');
9
- const match = raw.match(/(?:^|\n|\s)([$/]fraim)\s+([a-z0-9][a-z0-9-]*)(?=\s|$)/i);
9
+ const match = raw.match(/(?:^|\n|\s)([$/@]fraim)\s+([a-z0-9][a-z0-9-]*)(?=\s|$)/i);
10
10
  if (!match || match.index == null)
11
11
  return null;
12
12
  const before = raw.slice(0, match.index).trim();
13
13
  const after = raw.slice(match.index + match[0].length).trim();
14
14
  const remainder = [before, after].filter(Boolean).join('\n\n').trim();
15
15
  return {
16
- symbol: match[1].toLowerCase() === '$fraim' ? '$fraim' : '/fraim',
16
+ symbol: match[1].toLowerCase(),
17
17
  jobId: match[2].toLowerCase(),
18
18
  remainder,
19
19
  };
@@ -68,8 +68,10 @@ function normalizeProjectEntry(raw, fallbackPath) {
68
68
  return null;
69
69
  const folderPath = normalizeProjectPath(rawPath);
70
70
  const basename = path_1.default.basename(folderPath) || folderPath;
71
- const team = Array.isArray(value.team) ? value.team.filter((entry) => typeof entry === 'string') : [];
72
71
  const rawId = typeof value.id === 'string' ? value.id.trim() : '';
72
+ // The employee roster is deliberately dropped here (not read, not persisted):
73
+ // it is derived from the bootstrap's hired personas at render time so it stays
74
+ // consistent across machines. Any `team` on incoming/legacy state is discarded.
73
75
  return {
74
76
  id: rawId || projectIdForPath(folderPath),
75
77
  name: typeof value.name === 'string' && value.name.length > 0 ? value.name : basename,
@@ -78,7 +80,6 @@ function normalizeProjectEntry(raw, fallbackPath) {
78
80
  outcome: typeof value.outcome === 'string' ? value.outcome : '',
79
81
  rules: typeof value.rules === 'string' ? value.rules : '',
80
82
  brief: typeof value.brief === 'string' ? value.brief : (typeof value.intent === 'string' ? value.intent : ''),
81
- team,
82
83
  updatedAt: typeof value.updatedAt === 'string' ? value.updatedAt : undefined,
83
84
  };
84
85
  }
@@ -0,0 +1,88 @@
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;