fraim 2.0.165 → 2.0.167
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/catalog.js +20 -27
- package/dist/src/ai-hub/server.js +418 -2
- package/dist/src/ai-hub/word-sideload.js +95 -0
- package/dist/src/cli/commands/org.js +40 -0
- package/dist/src/cli/commands/test-mcp.js +171 -0
- package/dist/src/cli/fraim.js +2 -0
- package/dist/src/cli/setup/first-run.js +242 -0
- package/dist/src/cli/utils/org-publish.js +98 -0
- package/dist/src/config/ai-manager-hiring.js +121 -0
- package/dist/src/config/compat.js +16 -0
- package/dist/src/config/feature-flags.js +25 -0
- package/dist/src/config/persona-capability-bundles.js +273 -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/config-loader.js +9 -5
- package/dist/src/core/config-writer.js +75 -0
- package/dist/src/core/fraim-config-schema.generated.js +0 -21
- package/dist/src/core/utils/job-aliases.js +47 -0
- package/dist/src/core/utils/local-registry-resolver.js +8 -1
- package/dist/src/core/utils/workflow-parser.js +174 -0
- package/index.js +1 -1
- package/package.json +5 -1
- package/public/ai-hub/index.html +81 -0
- package/public/ai-hub/powerpoint-taskpane/index.html +236 -236
- package/public/ai-hub/powerpoint-taskpane/manifest.xml +29 -29
- package/public/ai-hub/review.css +13 -0
- package/public/ai-hub/script.js +414 -4
- package/public/ai-hub/styles.css +56 -0
- package/public/first-run/styles.css +73 -73
- 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,174 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.WorkflowParser = void 0;
|
|
4
|
+
const fs_1 = require("fs");
|
|
5
|
+
const path_1 = require("path");
|
|
6
|
+
class WorkflowParser {
|
|
7
|
+
static extractMetadataBlock(content) {
|
|
8
|
+
const frontmatterMatch = content.match(/^---\r?\n([\s\S]+?)\r?\n---/);
|
|
9
|
+
if (frontmatterMatch) {
|
|
10
|
+
try {
|
|
11
|
+
return {
|
|
12
|
+
state: 'valid',
|
|
13
|
+
metadata: JSON.parse(frontmatterMatch[1]),
|
|
14
|
+
bodyStartIndex: frontmatterMatch[0].length
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return { state: 'invalid' };
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
const trimmedStart = content.search(/\S/);
|
|
22
|
+
if (trimmedStart === -1 || content[trimmedStart] !== '{') {
|
|
23
|
+
return { state: 'none' };
|
|
24
|
+
}
|
|
25
|
+
let depth = 0;
|
|
26
|
+
let inString = false;
|
|
27
|
+
let escaping = false;
|
|
28
|
+
for (let i = trimmedStart; i < content.length; i++) {
|
|
29
|
+
const ch = content[i];
|
|
30
|
+
if (inString) {
|
|
31
|
+
if (escaping) {
|
|
32
|
+
escaping = false;
|
|
33
|
+
}
|
|
34
|
+
else if (ch === '\\') {
|
|
35
|
+
escaping = true;
|
|
36
|
+
}
|
|
37
|
+
else if (ch === '"') {
|
|
38
|
+
inString = false;
|
|
39
|
+
}
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
if (ch === '"') {
|
|
43
|
+
inString = true;
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
if (ch === '{') {
|
|
47
|
+
depth++;
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
if (ch === '}') {
|
|
51
|
+
depth--;
|
|
52
|
+
if (depth === 0) {
|
|
53
|
+
const bodyStartIndex = i + 1;
|
|
54
|
+
const remainder = content.slice(bodyStartIndex).trimStart();
|
|
55
|
+
// `{...}\n---` is usually malformed frontmatter, not bare JSON metadata.
|
|
56
|
+
if (remainder.startsWith('---')) {
|
|
57
|
+
return { state: 'none' };
|
|
58
|
+
}
|
|
59
|
+
try {
|
|
60
|
+
return {
|
|
61
|
+
state: 'valid',
|
|
62
|
+
metadata: JSON.parse(content.slice(trimmedStart, bodyStartIndex)),
|
|
63
|
+
bodyStartIndex
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
return { state: 'invalid' };
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return { state: 'none' };
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Parse a workflow markdown file into a structured definition
|
|
76
|
+
* Supports three formats:
|
|
77
|
+
* 1. Phase-based workflows with JSON frontmatter
|
|
78
|
+
* 2. Phase-based workflows with bare leading JSON metadata
|
|
79
|
+
* 3. Simple workflows without metadata
|
|
80
|
+
*/
|
|
81
|
+
static parse(filePath) {
|
|
82
|
+
if (!(0, fs_1.existsSync)(filePath))
|
|
83
|
+
return null;
|
|
84
|
+
let content = (0, fs_1.readFileSync)(filePath, 'utf-8');
|
|
85
|
+
if (content.charCodeAt(0) === 0xfeff) {
|
|
86
|
+
content = content.slice(1);
|
|
87
|
+
}
|
|
88
|
+
const metadataBlock = this.extractMetadataBlock(content);
|
|
89
|
+
if (metadataBlock.state === 'invalid') {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
if (metadataBlock.state === 'valid') {
|
|
93
|
+
return this.parsePhaseBasedWorkflow(filePath, content, metadataBlock.metadata, metadataBlock.bodyStartIndex);
|
|
94
|
+
}
|
|
95
|
+
return this.parseSimpleWorkflow(filePath, content);
|
|
96
|
+
}
|
|
97
|
+
static parsePhaseBasedWorkflow(filePath, content, metadata, bodyStartIndex) {
|
|
98
|
+
const contentAfterMetadata = content.substring(bodyStartIndex).trim();
|
|
99
|
+
const firstPhaseIndex = contentAfterMetadata.search(/^##\s+Phase:/m);
|
|
100
|
+
let overview = '';
|
|
101
|
+
let restOfContent = '';
|
|
102
|
+
if (firstPhaseIndex !== -1) {
|
|
103
|
+
overview = contentAfterMetadata.substring(0, firstPhaseIndex).trim();
|
|
104
|
+
restOfContent = contentAfterMetadata.substring(firstPhaseIndex);
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
overview = contentAfterMetadata;
|
|
108
|
+
}
|
|
109
|
+
const phases = new Map();
|
|
110
|
+
const phaseSections = restOfContent.split(/^##\s+Phase:\s+/m);
|
|
111
|
+
if (!metadata.phases) {
|
|
112
|
+
metadata.phases = {};
|
|
113
|
+
}
|
|
114
|
+
for (let i = 1; i < phaseSections.length; i++) {
|
|
115
|
+
const section = phaseSections[i];
|
|
116
|
+
const sectionLines = section.split('\n');
|
|
117
|
+
const firstLine = sectionLines[0].trim();
|
|
118
|
+
const id = firstLine.split(/[ (]/)[0].trim().toLowerCase();
|
|
119
|
+
phases.set(id, `## Phase: ${section.trim()}`);
|
|
120
|
+
}
|
|
121
|
+
return {
|
|
122
|
+
metadata,
|
|
123
|
+
overview,
|
|
124
|
+
phases,
|
|
125
|
+
isSimple: false,
|
|
126
|
+
path: filePath
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
static parseSimpleWorkflow(filePath, content) {
|
|
130
|
+
const workflowName = (0, path_1.basename)(filePath, '.md');
|
|
131
|
+
const metadata = {
|
|
132
|
+
name: workflowName
|
|
133
|
+
};
|
|
134
|
+
return {
|
|
135
|
+
metadata,
|
|
136
|
+
overview: content.trim(),
|
|
137
|
+
phases: new Map(),
|
|
138
|
+
isSimple: true,
|
|
139
|
+
path: filePath
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
static parseContent(content, name, path) {
|
|
143
|
+
if (content.charCodeAt(0) === 0xfeff) {
|
|
144
|
+
content = content.slice(1);
|
|
145
|
+
}
|
|
146
|
+
const metadataBlock = this.extractMetadataBlock(content);
|
|
147
|
+
if (metadataBlock.state === 'invalid') {
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
if (metadataBlock.state === 'valid') {
|
|
151
|
+
return this.parsePhaseBasedWorkflow(path || `content:${name}`, content, metadataBlock.metadata, metadataBlock.bodyStartIndex);
|
|
152
|
+
}
|
|
153
|
+
return this.parseSimpleWorkflow(path || `content:${name}`, content);
|
|
154
|
+
}
|
|
155
|
+
static getOverviewFromContent(content, name) {
|
|
156
|
+
const wf = this.parseContent(content, name);
|
|
157
|
+
return wf ? wf.overview : null;
|
|
158
|
+
}
|
|
159
|
+
static getOverview(filePath) {
|
|
160
|
+
const wf = this.parse(filePath);
|
|
161
|
+
return wf ? wf.overview : null;
|
|
162
|
+
}
|
|
163
|
+
static extractDescription(filePath) {
|
|
164
|
+
const wf = this.parse(filePath);
|
|
165
|
+
if (!wf)
|
|
166
|
+
return '';
|
|
167
|
+
const intentMatch = wf.overview.match(/## Intent\s+([\s\S]+?)(?:\r?\n##|$)/);
|
|
168
|
+
if (intentMatch)
|
|
169
|
+
return intentMatch[1].trim().split(/\r?\n/)[0];
|
|
170
|
+
const firstPara = wf.overview.split(/\r?\n/).find(l => l.trim() !== '' && !l.startsWith('#'));
|
|
171
|
+
return firstPara ? firstPara.trim() : '';
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
exports.WorkflowParser = WorkflowParser;
|
package/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fraim",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.167",
|
|
4
4
|
"description": "FRAIM CLI - Framework for Rigor-based AI Management (alias for fraim-framework)",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
@@ -100,12 +100,14 @@
|
|
|
100
100
|
"@types/cors": "^2.8.19",
|
|
101
101
|
"@types/express": "^5.0.6",
|
|
102
102
|
"@types/node": "^20.0.0",
|
|
103
|
+
"@types/node-cron": "^3.0.11",
|
|
103
104
|
"@types/node-fetch": "^2.6.13",
|
|
104
105
|
"@types/prompts": "^2.4.9",
|
|
105
106
|
"@types/semver": "^7.7.1",
|
|
106
107
|
"fast-glob": "^3.3.3",
|
|
107
108
|
"markdown-it": "^14.1.1",
|
|
108
109
|
"markdown-it-highlightjs": "^4.3.0",
|
|
110
|
+
"node-cron": "^4.2.1",
|
|
109
111
|
"playwright": "^1.58.2",
|
|
110
112
|
"pptxgenjs": "^4.0.1",
|
|
111
113
|
"puppeteer": "^24.36.1",
|
|
@@ -120,10 +122,12 @@
|
|
|
120
122
|
"dist/src/ai-hub/",
|
|
121
123
|
"dist/src/first-run/",
|
|
122
124
|
"dist/src/core/",
|
|
125
|
+
"dist/src/config/",
|
|
123
126
|
"bin/fraim.js",
|
|
124
127
|
"bin/fraim-mcp.js",
|
|
125
128
|
"public/ai-hub/",
|
|
126
129
|
"public/first-run/",
|
|
130
|
+
"public/portfolio/",
|
|
127
131
|
"index.js",
|
|
128
132
|
"README.md",
|
|
129
133
|
"CHANGELOG.md",
|
package/public/ai-hub/index.html
CHANGED
|
@@ -194,6 +194,18 @@
|
|
|
194
194
|
<span class="ca-note">— from <strong>sleep-on-learnings</strong> · promote to a scope</span></summary>
|
|
195
195
|
<div class="ctx-acc-body"><div id="project-learnings"></div></div>
|
|
196
196
|
</details>
|
|
197
|
+
<!-- Issue #578: Scheduled + Reactive Employees — deployment roster -->
|
|
198
|
+
<details class="ctx-acc" id="proj-deployments-acc">
|
|
199
|
+
<summary><span class="ca-chev">▸</span> <span>⏱ Deployments</span>
|
|
200
|
+
<span class="ca-note">— scheduled and webhook triggers for this project</span></summary>
|
|
201
|
+
<div class="ctx-acc-body">
|
|
202
|
+
<div id="proj-deployments-list"></div>
|
|
203
|
+
<div class="dep-actions">
|
|
204
|
+
<button class="dep-add-btn" id="dep-add-schedule-btn" type="button">+ Schedule</button>
|
|
205
|
+
<button class="dep-add-btn" id="dep-add-webhook-btn" type="button">+ Webhook</button>
|
|
206
|
+
</div>
|
|
207
|
+
</div>
|
|
208
|
+
</details>
|
|
197
209
|
</div>
|
|
198
210
|
|
|
199
211
|
|
|
@@ -668,6 +680,75 @@
|
|
|
668
680
|
</div>
|
|
669
681
|
</div>
|
|
670
682
|
|
|
683
|
+
<!-- Issue #578: Add Schedule deployment modal -->
|
|
684
|
+
<div id="dep-schedule-modal" class="modal-overlay" hidden>
|
|
685
|
+
<div class="modal-card" style="max-width:480px;">
|
|
686
|
+
<div class="modal-hdr">
|
|
687
|
+
<button class="modal-close" id="dep-schedule-close" type="button" aria-label="Close">×</button>
|
|
688
|
+
<h2>New Scheduled Deployment</h2>
|
|
689
|
+
</div>
|
|
690
|
+
<div class="modal-body">
|
|
691
|
+
<div class="hm-field">
|
|
692
|
+
<label for="dep-sch-label">Label</label>
|
|
693
|
+
<input type="text" id="dep-sch-label" placeholder="e.g. Nightly standup" />
|
|
694
|
+
</div>
|
|
695
|
+
<div class="hm-field">
|
|
696
|
+
<label for="dep-sch-job">Job</label>
|
|
697
|
+
<select id="dep-sch-job"></select>
|
|
698
|
+
</div>
|
|
699
|
+
<div class="hm-field">
|
|
700
|
+
<label for="dep-sch-cron">Cron expression</label>
|
|
701
|
+
<input type="text" id="dep-sch-cron" placeholder="e.g. 0 9 * * 1-5 (9am Mon-Fri)" />
|
|
702
|
+
</div>
|
|
703
|
+
<div class="hm-field">
|
|
704
|
+
<label for="dep-sch-instructions">Initial instructions (optional)</label>
|
|
705
|
+
<textarea id="dep-sch-instructions" rows="2" placeholder="Optional coaching message to send at run start"></textarea>
|
|
706
|
+
</div>
|
|
707
|
+
<div class="dep-modal-actions">
|
|
708
|
+
<button type="button" id="dep-sch-save-btn" class="send-button">Create schedule</button>
|
|
709
|
+
<button type="button" id="dep-sch-cancel-btn" class="cancel-button">Cancel</button>
|
|
710
|
+
</div>
|
|
711
|
+
<p id="dep-sch-error" class="dep-error" hidden></p>
|
|
712
|
+
</div>
|
|
713
|
+
</div>
|
|
714
|
+
</div>
|
|
715
|
+
|
|
716
|
+
<!-- Issue #578: Add Webhook deployment modal -->
|
|
717
|
+
<div id="dep-webhook-modal" class="modal-overlay" hidden>
|
|
718
|
+
<div class="modal-card" style="max-width:480px;">
|
|
719
|
+
<div class="modal-hdr">
|
|
720
|
+
<button class="modal-close" id="dep-webhook-close" type="button" aria-label="Close">×</button>
|
|
721
|
+
<h2>New Webhook Deployment</h2>
|
|
722
|
+
</div>
|
|
723
|
+
<div class="modal-body">
|
|
724
|
+
<div class="hm-field">
|
|
725
|
+
<label for="dep-wh-label">Label</label>
|
|
726
|
+
<input type="text" id="dep-wh-label" placeholder="e.g. ServiceNow escalation" />
|
|
727
|
+
</div>
|
|
728
|
+
<div class="hm-field">
|
|
729
|
+
<label for="dep-wh-job">Job</label>
|
|
730
|
+
<select id="dep-wh-job"></select>
|
|
731
|
+
</div>
|
|
732
|
+
<div class="hm-field">
|
|
733
|
+
<label for="dep-wh-instructions">Initial instructions (optional)</label>
|
|
734
|
+
<textarea id="dep-wh-instructions" rows="2" placeholder="Optional coaching message to prepend at run start"></textarea>
|
|
735
|
+
</div>
|
|
736
|
+
<div class="dep-modal-actions">
|
|
737
|
+
<button type="button" id="dep-wh-save-btn" class="send-button">Create webhook</button>
|
|
738
|
+
<button type="button" id="dep-wh-cancel-btn" class="cancel-button">Cancel</button>
|
|
739
|
+
</div>
|
|
740
|
+
<p id="dep-wh-error" class="dep-error" hidden></p>
|
|
741
|
+
<div id="dep-wh-inbound-row" hidden>
|
|
742
|
+
<label>Inbound URL (copy to your system)</label>
|
|
743
|
+
<div class="dep-inbound-url-row">
|
|
744
|
+
<code id="dep-wh-inbound-url" class="dep-inbound-url"></code>
|
|
745
|
+
<button type="button" id="dep-wh-copy-btn" class="hm-copy-btn">Copy</button>
|
|
746
|
+
</div>
|
|
747
|
+
</div>
|
|
748
|
+
</div>
|
|
749
|
+
</div>
|
|
750
|
+
</div>
|
|
751
|
+
|
|
671
752
|
<!-- Issue #539: Command palette overlay — replaces 2-step modal as job-launch surface -->
|
|
672
753
|
<div id="cp-modal" class="cp-backdrop" role="dialog" aria-modal="true" aria-label="Start a job" hidden>
|
|
673
754
|
<div class="cp-palette">
|