fraim-framework 2.0.147 → 2.0.149

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.
@@ -13,8 +13,6 @@ const os_1 = __importDefault(require("os"));
13
13
  const crypto_1 = require("crypto");
14
14
  const child_process_1 = require("child_process");
15
15
  const types_1 = require("../first-run/types");
16
- const persona_entitlement_service_1 = require("../services/persona-entitlement-service");
17
- const persona_capability_bundles_1 = require("../config/persona-capability-bundles");
18
16
  const PERSONA_AVATAR_SEEDS = {
19
17
  maestro: { seed: 'MAESTRO-founder-mode', bg: 'fde68a' },
20
18
  beza: { seed: 'BEZA-strategist', bg: 'c7d2fe' },
@@ -39,11 +37,51 @@ function buildPersonaAvatarUrl(personaKey) {
39
37
  const params = new URLSearchParams({ seed: data.seed, backgroundColor: data.bg, radius: '50' });
40
38
  return `https://api.dicebear.com/9.x/notionists/svg?${params.toString()}`;
41
39
  }
42
- const db_service_1 = require("../fraim/db-service");
43
40
  const catalog_1 = require("./catalog");
44
41
  const agent_token_prices_1 = require("../local-mcp-server/agent-token-prices");
45
42
  const hosts_1 = require("./hosts");
46
43
  const preferences_1 = require("./preferences");
44
+ function loadPersonaCapabilityModule() {
45
+ try {
46
+ // Server deployments include the persona catalog. The npm client package
47
+ // intentionally does not, so Hub setup must degrade without loading it.
48
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
49
+ return require('../config/persona-capability-bundles');
50
+ }
51
+ catch {
52
+ return null;
53
+ }
54
+ }
55
+ function getProtectedPersonaForHubJob(jobName) {
56
+ return loadPersonaCapabilityModule()?.getProtectedPersonaForJob(jobName) ?? null;
57
+ }
58
+ function listHubPersonaBundles() {
59
+ return loadPersonaCapabilityModule()?.listPersonaCapabilityBundles() ?? [];
60
+ }
61
+ function buildHubPersonaHireUrl(personaKey, hireMode = 'job') {
62
+ const params = new URLSearchParams({ persona: personaKey, mode: hireMode });
63
+ return `/pricing?${params.toString()}`;
64
+ }
65
+ async function getHubWorkspacePersonaState(dbService, userId, apiKey) {
66
+ try {
67
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
68
+ const service = require('../services/persona-entitlement-service');
69
+ return await service.getWorkspacePersonaState(dbService, userId, apiKey);
70
+ }
71
+ catch {
72
+ return null;
73
+ }
74
+ }
75
+ function createDefaultDbService() {
76
+ try {
77
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
78
+ const { FraimDbService } = require('../fraim/db-service');
79
+ return new FraimDbService();
80
+ }
81
+ catch {
82
+ return undefined;
83
+ }
84
+ }
47
85
  class AiHubRunRegistry {
48
86
  constructor() {
49
87
  this.runs = new Map();
@@ -358,13 +396,7 @@ class AiHubServer {
358
396
  this.dbService = options.dbService;
359
397
  }
360
398
  else {
361
- try {
362
- this.dbService = new db_service_1.FraimDbService();
363
- }
364
- catch {
365
- this.dbService = undefined;
366
- console.warn('[ai-hub] No dbService — personas will show as locked');
367
- }
399
+ this.dbService = createDefaultDbService();
368
400
  }
369
401
  this.app.use(express_1.default.json());
370
402
  this.app.use('/ai-hub', express_1.default.static(resolveAiHubPublicDir()));
@@ -424,7 +456,7 @@ class AiHubServer {
424
456
  const rawJobs = (0, catalog_1.discoverEmployeeJobs)(normalizedProjectPath);
425
457
  const jobs = rawJobs.map((job) => ({
426
458
  ...job,
427
- requiredPersonaKey: (0, persona_capability_bundles_1.getProtectedPersonaForJob)(job.id),
459
+ requiredPersonaKey: getProtectedPersonaForHubJob(job.id),
428
460
  }));
429
461
  const managerTemplates = (0, catalog_1.discoverManagerTemplates)(normalizedProjectPath);
430
462
  const { personas, subscriptionActive } = await this.computePersonas(apiKey || preferences.apiKey);
@@ -447,7 +479,7 @@ class AiHubServer {
447
479
  };
448
480
  }
449
481
  async computePersonas(apiKey) {
450
- const allBundles = (0, persona_capability_bundles_1.listPersonaCapabilityBundles)();
482
+ const allBundles = listHubPersonaBundles();
451
483
  const fallbackPersonas = allBundles.map((bundle) => ({
452
484
  key: bundle.personaKey,
453
485
  displayName: bundle.catalogMetadata.displayName,
@@ -455,12 +487,14 @@ class AiHubServer {
455
487
  avatarUrl: buildPersonaAvatarUrl(bundle.personaKey),
456
488
  pricingLabel: bundle.catalogMetadata.pricingLabel,
457
489
  status: 'locked',
458
- hireUrl: (0, persona_entitlement_service_1.buildPersonaHireUrl)(bundle.personaKey, bundle.defaultHireMode),
490
+ hireUrl: buildHubPersonaHireUrl(bundle.personaKey, bundle.defaultHireMode),
459
491
  }));
460
492
  if (!apiKey || !this.dbService)
461
493
  return { personas: fallbackPersonas, subscriptionActive: false };
462
494
  try {
463
- const state = await (0, persona_entitlement_service_1.getWorkspacePersonaState)(this.dbService, 'anonymous', apiKey);
495
+ const state = await getHubWorkspacePersonaState(this.dbService, 'anonymous', apiKey);
496
+ if (!state)
497
+ return { personas: fallbackPersonas, subscriptionActive: false };
464
498
  const hiredKeys = new Set(state.entitlements
465
499
  .filter((e) => e.status === 'active')
466
500
  .map((e) => e.personaKey));
@@ -471,7 +505,7 @@ class AiHubServer {
471
505
  avatarUrl: buildPersonaAvatarUrl(bundle.personaKey),
472
506
  pricingLabel: hiredKeys.has(bundle.personaKey) ? '' : bundle.catalogMetadata.pricingLabel,
473
507
  status: (hiredKeys.has(bundle.personaKey) ? 'hired' : 'locked'),
474
- hireUrl: (0, persona_entitlement_service_1.buildPersonaHireUrl)(bundle.personaKey, bundle.defaultHireMode),
508
+ hireUrl: buildHubPersonaHireUrl(bundle.personaKey, bundle.defaultHireMode),
475
509
  }));
476
510
  return { personas, subscriptionActive: state.subscriptionActive };
477
511
  }
@@ -613,7 +647,7 @@ class AiHubServer {
613
647
  phaseHistory: [],
614
648
  totals: emptyTotals(),
615
649
  lastStatusChangeAt: startTimestamp,
616
- personaKey: (0, persona_capability_bundles_1.getProtectedPersonaForJob)(jobId) ?? null,
650
+ personaKey: getProtectedPersonaForHubJob(jobId),
617
651
  // Gemini CLI does not emit a session ID in its output stream;
618
652
  // pre-seed one so the Send button is enabled and the continue
619
653
  // endpoint can proceed. continueRun for Gemini ignores it.
package/index.js CHANGED
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env node
2
2
 
3
3
  /**
4
4
  * FRAIM Framework - Smart Entry Point
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "fraim-framework",
3
- "version": "2.0.147",
3
+ "version": "2.0.149",
4
4
  "description": "FRAIM: AI Workforce Infrastructure — the organizational capability that turns AI agents into an accountable workforce, their operators into capable AI managers, and executives into leaders with clear optics on AI proficiency.",
5
5
  "main": "index.js",
6
6
  "bin": {
7
- "fraim": "./index.js",
8
- "fraim-framework": "./index.js"
7
+ "fraim": "./bin/fraim.js",
8
+ "fraim-framework": "./bin/fraim.js"
9
9
  },
10
10
  "scripts": {
11
11
  "dev": "tsx --watch src/fraim-mcp-server.ts > server.log 2>&1",
@@ -11,16 +11,16 @@
11
11
 
12
12
  <div class="page">
13
13
 
14
- <header class="header">
15
- <div class="header-copy">
16
- <span class="header-eyebrow">FRAIM Hub</span>
17
- <h1>AI Hub</h1>
18
- </div>
19
- <button class="project-button" type="button" id="project-button">
20
- <span class="folder">Project</span>
21
- <strong id="project-name">Choose a folder</strong>
22
- </button>
23
- </header>
14
+ <header class="header">
15
+ <div class="header-copy">
16
+ <span class="header-eyebrow">FRAIM Hub</span>
17
+ <h1>AI Hub</h1>
18
+ </div>
19
+ <button class="project-button" type="button" id="project-button">
20
+ <span class="folder">Project</span>
21
+ <strong id="project-name">Choose a folder</strong>
22
+ </button>
23
+ </header>
24
24
 
25
25
  <section class="welcome">
26
26
  Hi <strong class="you">there</strong>, remember, you are the
@@ -66,20 +66,20 @@
66
66
  </section>
67
67
 
68
68
  <div class="layout">
69
- <aside class="rail">
70
- <button class="new-conv" type="button" id="new-conv-btn">+ New job</button>
71
- <div class="rail-note">Alpha: browser shell for directing employees across your project.</div>
72
- <!-- R2.4: team roster — horizontal row of avatar chips per hired persona -->
73
- <section class="rail-section rail-section--employees">
74
- <div class="rail-section-label">Hired employees</div>
75
- <div class="team-roster" id="team-roster" hidden></div>
76
- </section>
77
-
78
- <section class="rail-section rail-section--runs">
79
- <div class="rail-section-label">Runs</div>
80
- <div class="conv-list" id="conv-list"></div>
81
- </section>
82
- </aside>
69
+ <aside class="rail">
70
+ <button class="new-conv" type="button" id="new-conv-btn">+ New job</button>
71
+ <div class="rail-note">Alpha: browser shell for directing employees across your project.</div>
72
+ <!-- R2.4: team roster — horizontal row of avatar chips per hired persona -->
73
+ <section class="rail-section rail-section--employees">
74
+ <div class="rail-section-label">Hired employees</div>
75
+ <div class="team-roster" id="team-roster" hidden></div>
76
+ </section>
77
+
78
+ <section class="rail-section rail-section--runs">
79
+ <div class="rail-section-label">Runs</div>
80
+ <div class="conv-list" id="conv-list"></div>
81
+ </section>
82
+ </aside>
83
83
 
84
84
  <main class="conversation" id="conversation">
85
85
  <div class="empty-state" id="empty">
@@ -87,25 +87,25 @@
87
87
  <p>Pick an existing job from the left, or click <strong>+ New job</strong> to give your employee something to work on.</p>
88
88
  </div>
89
89
 
90
- <div id="active-conv" hidden>
91
- <div class="conv-topline">
92
- <div class="employee-identity" id="active-identity"></div>
93
- <div class="run-state-pill" id="run-state-pill"></div>
94
- </div>
95
-
96
- <div class="conv-header">
97
- <div class="title-block">
98
- <h2 id="active-title"></h2>
99
- <div class="conv-job" id="active-job"></div>
100
- </div>
101
- <div id="artifact-slot"></div>
102
- </div>
103
-
104
- <div class="summary-strip" id="summary-strip"></div>
105
-
106
- <!-- Issue #347 R1: pizza tracker. Hidden when the active job
107
- declares no phases. Populated by renderTracker() in script.js. -->
108
- <div class="tracker" id="tracker" aria-label="Job progress" hidden>
90
+ <div id="active-conv" hidden>
91
+ <div class="conv-topline">
92
+ <div class="employee-identity" id="active-identity"></div>
93
+ <div class="run-state-pill" id="run-state-pill"></div>
94
+ </div>
95
+
96
+ <div class="conv-header">
97
+ <div class="title-block">
98
+ <h2 id="active-title"></h2>
99
+ <div class="conv-job" id="active-job"></div>
100
+ </div>
101
+ <div id="artifact-slot"></div>
102
+ </div>
103
+
104
+ <div class="summary-strip" id="summary-strip"></div>
105
+
106
+ <!-- Issue #347 R1: pizza tracker. Hidden when the active job
107
+ declares no phases. Populated by renderTracker() in script.js. -->
108
+ <div class="tracker" id="tracker" aria-label="Job progress" hidden>
109
109
  <div class="tracker-rows" id="tracker-rows"></div>
110
110
  <div class="tracker-note" id="tracker-note" hidden></div>
111
111
  </div>
@@ -115,32 +115,32 @@
115
115
  <span class="latest" id="latest"></span>
116
116
  </div>
117
117
 
118
- <section class="thread-surface" aria-label="Manager and employee thread">
119
- <div class="thread-surface-label">Manager and employee thread</div>
120
- <div class="messages" id="messages"></div>
121
- </section>
118
+ <section class="thread-surface" aria-label="Manager and employee thread">
119
+ <div class="thread-surface-label">Manager and employee thread</div>
120
+ <div class="messages" id="messages"></div>
121
+ </section>
122
122
 
123
123
  <div class="coach">
124
- <div class="coach-title-row">
125
- <span class="section-title">Coach the employee</span>
126
- <span class="active-employee-row">
127
- <label for="active-employee-select" class="active-employee-label">tool</label>
128
- <select id="active-employee-select" class="employee-select inline"></select>
129
- </span>
130
- </div>
131
- <textarea id="coach-text" placeholder="Tell the employee what to do next…"></textarea>
132
- <div class="coach-actions">
133
- <!-- Issue #347 R2: template picker. Hidden when the project
134
- has no manager-job templates. -->
135
- <button class="ghost" type="button" id="template-picker-btn" aria-haspopup="menu" aria-expanded="false" hidden>Use a template ▾</button>
136
- <button class="send-button" type="button" id="send" disabled>Send</button>
137
- <div class="template-popover" id="template-popover" role="menu" hidden></div>
138
- </div>
139
- <div class="coach-note" id="coach-note"></div>
140
- <!-- Issue #347 R4: run-level totals. Discoverable, not dominating.
141
- Populated by renderTotals() each poll tick. -->
142
- <div class="totals" id="totals" aria-label="Run totals" hidden></div>
143
- </div>
124
+ <div class="coach-title-row">
125
+ <span class="section-title">Coach the employee</span>
126
+ <span class="active-employee-row">
127
+ <label for="active-employee-select" class="active-employee-label">tool</label>
128
+ <select id="active-employee-select" class="employee-select inline"></select>
129
+ </span>
130
+ </div>
131
+ <textarea id="coach-text" placeholder="Tell the employee what to do next…"></textarea>
132
+ <div class="coach-actions">
133
+ <!-- Issue #347 R2: template picker. Hidden when the project
134
+ has no manager-job templates. -->
135
+ <button class="ghost" type="button" id="template-picker-btn" aria-haspopup="menu" aria-expanded="false" hidden>Use a template ▾</button>
136
+ <button class="send-button" type="button" id="send" disabled>Send</button>
137
+ <div class="template-popover" id="template-popover" role="menu" hidden></div>
138
+ </div>
139
+ <div class="coach-note" id="coach-note"></div>
140
+ <!-- Issue #347 R4: run-level totals. Discoverable, not dominating.
141
+ Populated by renderTotals() each poll tick. -->
142
+ <div class="totals" id="totals" aria-label="Run totals" hidden></div>
143
+ </div>
144
144
 
145
145
  <details class="micro" id="micro-manage">
146
146
  <summary>Micro-manage — raw host details</summary>