fraim-framework 2.0.166 → 2.0.168

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 (60) hide show
  1. package/dist/src/ai-hub/catalog.js +43 -36
  2. package/dist/src/ai-hub/server.js +28 -5
  3. package/dist/src/cli/commands/init-project.js +1 -98
  4. package/dist/src/cli/commands/manager.js +40 -0
  5. package/dist/src/cli/commands/sync.js +17 -21
  6. package/dist/src/cli/fraim.js +2 -0
  7. package/dist/src/cli/utils/github-workflow-sync.js +12 -146
  8. package/dist/src/cli/utils/manager-pack-sync.js +188 -0
  9. package/dist/src/cli/utils/manager-publish.js +76 -0
  10. package/dist/src/cli/utils/user-config.js +20 -0
  11. package/dist/src/core/config-loader.js +9 -5
  12. package/dist/src/core/fraim-config-schema.generated.js +85 -31
  13. package/dist/src/core/manager-pack.js +26 -0
  14. package/dist/src/core/utils/local-registry-resolver.js +8 -1
  15. package/dist/src/first-run/install-state.js +1 -0
  16. package/dist/src/first-run/server.js +9 -0
  17. package/dist/src/first-run/session-service.js +117 -23
  18. package/dist/src/first-run/types.js +2 -5
  19. package/dist/src/local-mcp-server/learning-context-builder.js +45 -8
  20. package/dist/src/local-mcp-server/stdio-server.js +28 -0
  21. package/index.js +1 -1
  22. package/package.json +4 -1
  23. package/public/ai-hub/powerpoint-taskpane/index.html +236 -236
  24. package/public/ai-hub/powerpoint-taskpane/manifest.xml +29 -29
  25. package/public/ai-hub/review.css +13 -0
  26. package/public/ai-hub/script.js +199 -5
  27. package/public/ai-hub/styles.css +28 -0
  28. package/public/first-run/index.html +1 -1
  29. package/public/first-run/script.js +459 -530
  30. package/public/first-run/styles.css +288 -73
  31. package/public/portfolio/ashley.html +523 -0
  32. package/public/portfolio/auditya.html +83 -0
  33. package/public/portfolio/banke.html +83 -0
  34. package/public/portfolio/beza.html +659 -0
  35. package/public/portfolio/careena.html +632 -0
  36. package/public/portfolio/casey.html +568 -0
  37. package/public/portfolio/celia.html +490 -0
  38. package/public/portfolio/deidre.html +642 -0
  39. package/public/portfolio/gautam.html +597 -0
  40. package/public/portfolio/hari.html +469 -0
  41. package/public/portfolio/huxley.html +1354 -0
  42. package/public/portfolio/index.html +741 -0
  43. package/public/portfolio/maestro.html +518 -0
  44. package/public/portfolio/mandy.html +590 -0
  45. package/public/portfolio/mona.html +597 -0
  46. package/public/portfolio/pam.html +887 -0
  47. package/public/portfolio/procella.html +107 -0
  48. package/public/portfolio/qasm.html +569 -0
  49. package/public/portfolio/ricardo.html +489 -0
  50. package/public/portfolio/sade.html +560 -0
  51. package/public/portfolio/sam.html +654 -0
  52. package/public/portfolio/sechar.html +580 -0
  53. package/public/portfolio/sreya.html +599 -0
  54. package/public/portfolio/swen.html +601 -0
  55. package/dist/src/ai-hub/word-sideload.js +0 -95
  56. package/dist/src/cli/commands/test-mcp.js +0 -171
  57. package/dist/src/cli/setup/first-run.js +0 -242
  58. package/dist/src/core/config-writer.js +0 -75
  59. package/dist/src/core/utils/job-aliases.js +0 -47
  60. package/dist/src/core/utils/workflow-parser.js +0 -174
@@ -3,67 +3,15 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.GITHUB_WORKFLOW_ASSET_PREFIX = exports.GITHUB_WORKFLOW_HEADER = exports.GITHUB_WORKFLOW_MANIFEST_RELATIVE_PATH = void 0;
7
- exports.isGitHubWorkflowAutomationEnabled = isGitHubWorkflowAutomationEnabled;
8
- exports.decorateManagedGitHubWorkflow = decorateManagedGitHubWorkflow;
9
- exports.isFraimManagedGitHubWorkflow = isFraimManagedGitHubWorkflow;
10
- exports.loadGitHubWorkflowManifest = loadGitHubWorkflowManifest;
11
6
  exports.loadLocalGitHubWorkflowBundle = loadLocalGitHubWorkflowBundle;
12
7
  exports.fetchGitHubWorkflowBundle = fetchGitHubWorkflowBundle;
13
- exports.reconcileGitHubWorkflowAssets = reconcileGitHubWorkflowAssets;
8
+ exports.installGitHubWorkflowAssets = installGitHubWorkflowAssets;
14
9
  exports.formatGitHubWorkflowSyncSummary = formatGitHubWorkflowSyncSummary;
15
10
  const axios_1 = __importDefault(require("axios"));
16
- const crypto_1 = __importDefault(require("crypto"));
17
11
  const fs_1 = __importDefault(require("fs"));
18
12
  const path_1 = __importDefault(require("path"));
19
13
  const config_loader_1 = require("../../core/config-loader");
20
14
  const project_fraim_paths_1 = require("../../core/utils/project-fraim-paths");
21
- exports.GITHUB_WORKFLOW_MANIFEST_RELATIVE_PATH = path_1.default.join('fraim', 'managed-assets', 'github-workflows.json');
22
- exports.GITHUB_WORKFLOW_HEADER = '# FRAIM-MANAGED: github-workflow';
23
- exports.GITHUB_WORKFLOW_ASSET_PREFIX = '# FRAIM-ASSET-ID: ';
24
- function sha256(content) {
25
- return `sha256:${crypto_1.default.createHash('sha256').update(content).digest('hex')}`;
26
- }
27
- function getManifestPath(projectRoot) {
28
- return path_1.default.join(projectRoot, exports.GITHUB_WORKFLOW_MANIFEST_RELATIVE_PATH);
29
- }
30
- function isGitHubWorkflowAutomationEnabled(config) {
31
- return config?.customizations?.githubWorkflows?.enabled === true;
32
- }
33
- function decorateManagedGitHubWorkflow(assetId, content) {
34
- const normalized = content.replace(/^\uFEFF/, '');
35
- const assetHeader = `${exports.GITHUB_WORKFLOW_HEADER}\n${exports.GITHUB_WORKFLOW_ASSET_PREFIX}${assetId}`;
36
- if (normalized.includes(exports.GITHUB_WORKFLOW_HEADER) && normalized.includes(`${exports.GITHUB_WORKFLOW_ASSET_PREFIX}${assetId}`)) {
37
- return normalized;
38
- }
39
- return `${assetHeader}\n${normalized}`;
40
- }
41
- function isFraimManagedGitHubWorkflow(content, assetId) {
42
- if (!content.includes(exports.GITHUB_WORKFLOW_HEADER)) {
43
- return false;
44
- }
45
- if (!assetId) {
46
- return true;
47
- }
48
- return content.includes(`${exports.GITHUB_WORKFLOW_ASSET_PREFIX}${assetId}`);
49
- }
50
- function loadGitHubWorkflowManifest(projectRoot) {
51
- const manifestPath = getManifestPath(projectRoot);
52
- if (!fs_1.default.existsSync(manifestPath)) {
53
- return {
54
- version: 1,
55
- enabled: true,
56
- provider: 'github',
57
- managedFiles: []
58
- };
59
- }
60
- return JSON.parse(fs_1.default.readFileSync(manifestPath, 'utf8'));
61
- }
62
- function writeGitHubWorkflowManifest(projectRoot, manifest) {
63
- const manifestPath = getManifestPath(projectRoot);
64
- fs_1.default.mkdirSync(path_1.default.dirname(manifestPath), { recursive: true });
65
- fs_1.default.writeFileSync(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`, 'utf8');
66
- }
67
15
  function loadLocalGitHubWorkflowBundle(registryRoot) {
68
16
  const workflowsRoot = path_1.default.join(registryRoot, 'github', 'workflows');
69
17
  if (!fs_1.default.existsSync(workflowsRoot)) {
@@ -77,8 +25,7 @@ function loadLocalGitHubWorkflowBundle(registryRoot) {
77
25
  return {
78
26
  path: entry.name,
79
27
  content,
80
- type: 'github-workflow',
81
- digest: sha256(content)
28
+ type: 'github-workflow'
82
29
  };
83
30
  });
84
31
  return files.sort((a, b) => a.path.localeCompare(b.path));
@@ -98,7 +45,7 @@ function readProjectConfig(projectRoot, config) {
98
45
  }
99
46
  return (0, config_loader_1.loadFraimConfig)((0, project_fraim_paths_1.getWorkspaceConfigPath)(projectRoot));
100
47
  }
101
- function reconcileGitHubWorkflowAssets(options) {
48
+ function installGitHubWorkflowAssets(options) {
102
49
  const config = readProjectConfig(options.projectRoot, options.config);
103
50
  const provider = config.repository?.provider || null;
104
51
  if (provider !== 'github') {
@@ -109,108 +56,27 @@ function reconcileGitHubWorkflowAssets(options) {
109
56
  results: []
110
57
  };
111
58
  }
112
- if (!isGitHubWorkflowAutomationEnabled(config)) {
113
- return {
114
- enabled: false,
115
- provider,
116
- skippedReason: 'GitHub workflow automation disabled',
117
- results: []
118
- };
119
- }
120
- const manifest = loadGitHubWorkflowManifest(options.projectRoot);
121
- const manifestEntries = new Map(manifest.managedFiles.map((entry) => [entry.path, entry]));
122
59
  const workflowsDir = path_1.default.join(options.projectRoot, '.github', 'workflows');
123
60
  fs_1.default.mkdirSync(workflowsDir, { recursive: true });
124
61
  const results = [];
125
- const nextManagedFiles = [];
126
62
  for (const file of options.files) {
127
- const assetId = path_1.default.basename(file.path);
128
63
  const relativeWorkflowPath = path_1.default.join('.github', 'workflows', file.path).replace(/[\\/]/g, '/');
129
- const destinationPath = path_1.default.join(options.projectRoot, '.github', 'workflows', file.path);
130
- const desiredContent = decorateManagedGitHubWorkflow(assetId, file.content);
131
- const desiredInstalledDigest = sha256(desiredContent);
132
- const previousEntry = manifestEntries.get(relativeWorkflowPath);
133
- if (!fs_1.default.existsSync(destinationPath)) {
134
- fs_1.default.mkdirSync(path_1.default.dirname(destinationPath), { recursive: true });
135
- fs_1.default.writeFileSync(destinationPath, desiredContent, 'utf8');
136
- nextManagedFiles.push({
137
- path: relativeWorkflowPath,
138
- assetId,
139
- sourceDigest: file.digest,
140
- installedDigest: desiredInstalledDigest
141
- });
142
- results.push({
143
- path: relativeWorkflowPath,
144
- assetId,
145
- status: 'installed'
146
- });
147
- continue;
148
- }
149
- const currentContent = fs_1.default.readFileSync(destinationPath, 'utf8');
150
- const currentDigest = sha256(currentContent);
151
- const ownedByFraim = isFraimManagedGitHubWorkflow(currentContent, assetId) || Boolean(previousEntry);
152
- if (!ownedByFraim) {
153
- if (previousEntry) {
154
- nextManagedFiles.push(previousEntry);
155
- }
156
- results.push({
157
- path: relativeWorkflowPath,
158
- assetId,
159
- status: 'conflict',
160
- reason: 'same-named file exists without FRAIM ownership marker'
161
- });
162
- continue;
163
- }
164
- if (currentDigest === desiredInstalledDigest) {
165
- nextManagedFiles.push({
166
- path: relativeWorkflowPath,
167
- assetId,
168
- sourceDigest: file.digest,
169
- installedDigest: desiredInstalledDigest
170
- });
64
+ const destinationPath = path_1.default.join(workflowsDir, file.path);
65
+ if (fs_1.default.existsSync(destinationPath)) {
171
66
  results.push({
172
67
  path: relativeWorkflowPath,
173
- assetId,
174
- status: 'already current'
68
+ status: 'skipped',
69
+ reason: 'already exists'
175
70
  });
176
71
  continue;
177
72
  }
178
- if (previousEntry && currentDigest === previousEntry.installedDigest) {
179
- fs_1.default.writeFileSync(destinationPath, desiredContent, 'utf8');
180
- nextManagedFiles.push({
181
- path: relativeWorkflowPath,
182
- assetId,
183
- sourceDigest: file.digest,
184
- installedDigest: desiredInstalledDigest
185
- });
186
- results.push({
187
- path: relativeWorkflowPath,
188
- assetId,
189
- status: 'updated'
190
- });
191
- continue;
192
- }
193
- nextManagedFiles.push(previousEntry || {
194
- path: relativeWorkflowPath,
195
- assetId,
196
- sourceDigest: file.digest,
197
- installedDigest: currentDigest
198
- });
73
+ fs_1.default.mkdirSync(path_1.default.dirname(destinationPath), { recursive: true });
74
+ fs_1.default.writeFileSync(destinationPath, file.content, 'utf8');
199
75
  results.push({
200
76
  path: relativeWorkflowPath,
201
- assetId,
202
- status: 'conflict',
203
- reason: previousEntry
204
- ? 'managed file has local edits that do not match the last installed digest'
205
- : 'FRAIM-managed header exists without manifest baseline'
77
+ status: 'installed'
206
78
  });
207
79
  }
208
- writeGitHubWorkflowManifest(options.projectRoot, {
209
- version: 1,
210
- enabled: true,
211
- provider: 'github',
212
- managedFiles: nextManagedFiles.sort((a, b) => a.path.localeCompare(b.path))
213
- });
214
80
  return {
215
81
  enabled: true,
216
82
  provider,
@@ -219,10 +85,10 @@ function reconcileGitHubWorkflowAssets(options) {
219
85
  }
220
86
  function formatGitHubWorkflowSyncSummary(result) {
221
87
  if (!result.enabled) {
222
- return [`GitHub workflow reconciliation skipped: ${result.skippedReason || 'not enabled'}`];
88
+ return [`GitHub workflow install skipped: ${result.skippedReason || 'not enabled'}`];
223
89
  }
224
90
  if (result.results.length === 0) {
225
- return ['GitHub workflow reconciliation: no workflow assets found'];
91
+ return ['GitHub workflow install: no workflow assets found'];
226
92
  }
227
93
  return result.results.map((item) => {
228
94
  const suffix = item.reason ? ` (${item.reason})` : '';
@@ -0,0 +1,188 @@
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.MANAGER_CACHE_MANAGED_HEADER = exports.MANAGER_SYNC_METADATA_FILE = exports.MANAGER_CACHE_DIRNAME = void 0;
7
+ exports.getManagerCacheDir = getManagerCacheDir;
8
+ exports.readManagerCacheMetadata = readManagerCacheMetadata;
9
+ exports.getManagerCacheAgeHours = getManagerCacheAgeHours;
10
+ exports.syncManagerCache = syncManagerCache;
11
+ /**
12
+ * Manager cache sync (issue #580).
13
+ *
14
+ * Materializes the manager's personal context/rules/learnings into the managed
15
+ * cache at ~/.fraim/manager/, from either backend:
16
+ * - git: shallow snapshot of a manager-owned repo
17
+ * - fraim-cloud: GET /api/manager/pack from the FRAIM server
18
+ *
19
+ * Cache files are managed content: marked, overwritten on every sync, and
20
+ * never a direct write target for agents.
21
+ */
22
+ const axios_1 = __importDefault(require("axios"));
23
+ const fs_1 = __importDefault(require("fs"));
24
+ const path_1 = __importDefault(require("path"));
25
+ const project_fraim_paths_1 = require("../../core/utils/project-fraim-paths");
26
+ const manager_pack_1 = require("../../core/manager-pack");
27
+ const git_org_sync_1 = require("./git-org-sync");
28
+ const user_config_1 = require("./user-config");
29
+ exports.MANAGER_CACHE_DIRNAME = 'manager';
30
+ exports.MANAGER_SYNC_METADATA_FILE = '.manager-sync-metadata.json';
31
+ exports.MANAGER_CACHE_MANAGED_HEADER = '<!-- FRAIM_MANAGER_SYNC_MANAGED_CONTENT -->';
32
+ const MANAGER_PACK_DIRS = ['context', 'rules', 'learnings'];
33
+ function getManagerCacheDir() {
34
+ return path_1.default.join((0, project_fraim_paths_1.getUserFraimDirPath)(), exports.MANAGER_CACHE_DIRNAME);
35
+ }
36
+ function readManagerCacheMetadata() {
37
+ try {
38
+ const metadataPath = path_1.default.join(getManagerCacheDir(), exports.MANAGER_SYNC_METADATA_FILE);
39
+ if (!fs_1.default.existsSync(metadataPath))
40
+ return null;
41
+ return JSON.parse(fs_1.default.readFileSync(metadataPath, 'utf8'));
42
+ }
43
+ catch {
44
+ return null;
45
+ }
46
+ }
47
+ function getManagerCacheAgeHours() {
48
+ const metadata = readManagerCacheMetadata();
49
+ if (!metadata?.syncedAt)
50
+ return null;
51
+ const syncedAt = Date.parse(metadata.syncedAt);
52
+ if (Number.isNaN(syncedAt))
53
+ return null;
54
+ return Math.max(0, (Date.now() - syncedAt) / 3_600_000);
55
+ }
56
+ function decorateManagedManagerFile(content, backend) {
57
+ const normalized = content.replace(/^\uFEFF/, '');
58
+ if (normalized.startsWith(exports.MANAGER_CACHE_MANAGED_HEADER))
59
+ return normalized;
60
+ const writePath = backend === 'git'
61
+ ? 'open a pull request against your manager storage repo'
62
+ : 'publish the change through your FRAIM manager storage flow';
63
+ const marker = [
64
+ exports.MANAGER_CACHE_MANAGED_HEADER,
65
+ '> [!IMPORTANT]',
66
+ '> Synced from your personal manager storage. Local edits are overwritten on the next sync.',
67
+ `> To change this content, ${writePath}.`,
68
+ ''
69
+ ].join('\n');
70
+ return `${marker}\n${normalized}`;
71
+ }
72
+ function collectGitPackFiles(snapshotDir) {
73
+ const files = [];
74
+ for (const dirName of MANAGER_PACK_DIRS) {
75
+ const dirPath = path_1.default.join(snapshotDir, dirName);
76
+ if (!fs_1.default.existsSync(dirPath))
77
+ continue;
78
+ for (const entry of fs_1.default.readdirSync(dirPath, { withFileTypes: true })) {
79
+ if (!entry.isFile() || !entry.name.endsWith('.md'))
80
+ continue;
81
+ files.push({
82
+ relativePath: `${dirName}/${entry.name}`,
83
+ content: fs_1.default.readFileSync(path_1.default.join(dirPath, entry.name), 'utf8')
84
+ });
85
+ }
86
+ }
87
+ return files;
88
+ }
89
+ async function fetchCloudPack(remoteUrl, apiKey) {
90
+ const response = await axios_1.default.get(`${remoteUrl.replace(/\/$/, '')}/api/manager/pack`, {
91
+ headers: { 'x-api-key': apiKey },
92
+ timeout: 30_000
93
+ });
94
+ const files = Array.isArray(response.data?.files) ? response.data.files : [];
95
+ return {
96
+ files: files.filter((f) => typeof f?.relativePath === 'string' &&
97
+ (0, manager_pack_1.isManagerPackRelativePath)(f.relativePath) &&
98
+ typeof f?.content === 'string'),
99
+ version: String(response.data?.version ?? '0')
100
+ };
101
+ }
102
+ function materializeCache(files, metadata) {
103
+ const cacheDir = getManagerCacheDir();
104
+ const stagingDir = `${cacheDir}.staging-${process.pid}`;
105
+ fs_1.default.rmSync(stagingDir, { recursive: true, force: true });
106
+ fs_1.default.mkdirSync(stagingDir, { recursive: true });
107
+ for (const file of files) {
108
+ if (!(0, manager_pack_1.isManagerPackRelativePath)(file.relativePath))
109
+ continue;
110
+ const destination = path_1.default.join(stagingDir, file.relativePath);
111
+ fs_1.default.mkdirSync(path_1.default.dirname(destination), { recursive: true });
112
+ fs_1.default.writeFileSync(destination, decorateManagedManagerFile(file.content, metadata.backend));
113
+ }
114
+ fs_1.default.writeFileSync(path_1.default.join(stagingDir, exports.MANAGER_SYNC_METADATA_FILE), JSON.stringify(metadata, null, 2));
115
+ fs_1.default.rmSync(cacheDir, { recursive: true, force: true });
116
+ fs_1.default.renameSync(stagingDir, cacheDir);
117
+ }
118
+ async function discoverCloudManagerStorage(options) {
119
+ try {
120
+ const apiKey = options?.apiKey || (0, user_config_1.readUserFraimConfig)().apiKey;
121
+ if (!apiKey || apiKey === 'local-dev' || apiKey === 'test-mode-key')
122
+ return null;
123
+ const remoteUrl = options?.remoteUrl || process.env.FRAIM_REMOTE_URL || 'https://fraim.wellnessatwork.me';
124
+ const pack = await fetchCloudPack(remoteUrl, apiKey);
125
+ if (pack.files.length === 0)
126
+ return null;
127
+ (0, user_config_1.writeUserFraimConfig)({ managerStorage: { backend: 'fraim-cloud' } });
128
+ return (0, user_config_1.getManagerStorageConfig)();
129
+ }
130
+ catch {
131
+ return null;
132
+ }
133
+ }
134
+ function failureOutcome(error) {
135
+ const message = error instanceof Error ? error.message : String(error);
136
+ const existing = readManagerCacheMetadata();
137
+ if (existing) {
138
+ return {
139
+ status: 'stale',
140
+ metadata: existing,
141
+ ageHours: getManagerCacheAgeHours() ?? 0,
142
+ error: message
143
+ };
144
+ }
145
+ return { status: 'absent', error: message };
146
+ }
147
+ async function syncManagerCache(options) {
148
+ let managerStorage = (0, user_config_1.getManagerStorageConfig)();
149
+ if (!managerStorage) {
150
+ managerStorage = await discoverCloudManagerStorage(options);
151
+ if (!managerStorage)
152
+ return { status: 'disabled' };
153
+ }
154
+ try {
155
+ if (managerStorage.backend === 'git') {
156
+ const snapshot = (0, git_org_sync_1.fetchOrgRepoSnapshot)(managerStorage.gitUrl);
157
+ try {
158
+ const metadata = {
159
+ version: snapshot.sha,
160
+ backend: 'git',
161
+ source: managerStorage.gitUrl,
162
+ syncedAt: new Date().toISOString()
163
+ };
164
+ materializeCache(collectGitPackFiles(snapshot.dir), metadata);
165
+ return { status: 'synced', metadata };
166
+ }
167
+ finally {
168
+ snapshot.cleanup();
169
+ }
170
+ }
171
+ const remoteUrl = options?.remoteUrl || process.env.FRAIM_REMOTE_URL || 'https://fraim.wellnessatwork.me';
172
+ const apiKey = options?.apiKey || (0, user_config_1.readUserFraimConfig)().apiKey;
173
+ if (!apiKey)
174
+ throw new Error('No FRAIM API key available for the fraim-cloud manager backend.');
175
+ const pack = await fetchCloudPack(remoteUrl, apiKey);
176
+ const metadata = {
177
+ version: pack.version,
178
+ backend: 'fraim-cloud',
179
+ source: remoteUrl,
180
+ syncedAt: new Date().toISOString()
181
+ };
182
+ materializeCache(pack.files, metadata);
183
+ return { status: 'synced', metadata };
184
+ }
185
+ catch (error) {
186
+ return failureOutcome(error);
187
+ }
188
+ }
@@ -0,0 +1,76 @@
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.managerPackRelativePathFor = managerPackRelativePathFor;
7
+ exports.publishManagerArtifacts = publishManagerArtifacts;
8
+ /**
9
+ * Publish manager context/rules/learnings to the configured manager backend
10
+ * (issue #580).
11
+ */
12
+ const axios_1 = __importDefault(require("axios"));
13
+ const child_process_1 = require("child_process");
14
+ const fs_1 = __importDefault(require("fs"));
15
+ const os_1 = __importDefault(require("os"));
16
+ const path_1 = __importDefault(require("path"));
17
+ const manager_pack_1 = require("../../core/manager-pack");
18
+ const user_config_1 = require("./user-config");
19
+ const ALLOWED_GIT_URL = /^(https?:\/\/|ssh:\/\/|git:\/\/|file:\/\/|[\w.-]+@[\w.-]+:)/;
20
+ function managerPackRelativePathFor(filePath) {
21
+ const relativePath = (0, manager_pack_1.managerPackRelativePathForFileName)(filePath);
22
+ if (relativePath)
23
+ return relativePath;
24
+ const base = path_1.default.basename(filePath);
25
+ throw new Error(`Cannot infer manager-pack location for '${base}' (expected manager_context.md, manager_rules.md, or a personal learning file)`);
26
+ }
27
+ function gitCompareUrl(gitUrl, branch) {
28
+ const httpUrl = gitUrl.replace(/\.git$/, '').replace(/^git@([^:]+):/, 'https://$1/');
29
+ if (/^https?:\/\//.test(httpUrl))
30
+ return `${httpUrl}/compare/${branch}?expand=1`;
31
+ return undefined;
32
+ }
33
+ async function publishManagerArtifacts(artifacts, opts) {
34
+ const managerStorage = (0, user_config_1.getManagerStorageConfig)();
35
+ if (!managerStorage) {
36
+ throw new Error('No manager storage backend configured. Set the managerStorage block in ~/.fraim/config.json.');
37
+ }
38
+ if (artifacts.length === 0)
39
+ throw new Error('No artifacts to publish.');
40
+ for (const artifact of artifacts) {
41
+ if (!(0, manager_pack_1.isManagerPackRelativePath)(artifact.relativePath)) {
42
+ throw new Error(`Invalid manager-pack path '${artifact.relativePath}' (must be context/manager_context.md, rules/manager_rules.md, or learnings/<user>-*.md).`);
43
+ }
44
+ }
45
+ if (managerStorage.backend === 'fraim-cloud') {
46
+ const remoteUrl = opts?.remoteUrl || process.env.FRAIM_REMOTE_URL || 'https://fraim.wellnessatwork.me';
47
+ const apiKey = opts?.apiKey || (0, user_config_1.readUserFraimConfig)().apiKey;
48
+ if (!apiKey)
49
+ throw new Error('No FRAIM API key available for the fraim-cloud manager backend.');
50
+ const res = await axios_1.default.post(`${remoteUrl.replace(/\/$/, '')}/api/manager/publish`, { artifacts }, { headers: { 'x-api-key': apiKey }, timeout: 30_000 });
51
+ return { backend: 'fraim-cloud', version: String(res.data?.version ?? '') };
52
+ }
53
+ const gitUrl = managerStorage.gitUrl;
54
+ if (!ALLOWED_GIT_URL.test(gitUrl))
55
+ throw new Error(`Manager repo URL has an unsupported scheme: ${gitUrl}`);
56
+ const dir = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'fraim-manager-pub-'));
57
+ try {
58
+ (0, child_process_1.execFileSync)('git', ['clone', '--depth=1', '--quiet', '--', gitUrl, '.'], {
59
+ cwd: dir, stdio: ['ignore', 'pipe', 'pipe'], timeout: 60_000
60
+ });
61
+ const branch = `fraim-manager-update-${opts?.branchSuffix || `${process.pid}-${Date.now()}`}`;
62
+ (0, child_process_1.execFileSync)('git', ['checkout', '-b', branch], { cwd: dir, stdio: ['ignore', 'pipe', 'pipe'] });
63
+ for (const artifact of artifacts) {
64
+ const dest = path_1.default.join(dir, artifact.relativePath);
65
+ fs_1.default.mkdirSync(path_1.default.dirname(dest), { recursive: true });
66
+ fs_1.default.writeFileSync(dest, artifact.content);
67
+ }
68
+ (0, child_process_1.execFileSync)('git', ['add', '-A'], { cwd: dir, stdio: ['ignore', 'pipe', 'pipe'] });
69
+ (0, child_process_1.execFileSync)('git', ['commit', '-m', 'Update manager context'], { cwd: dir, stdio: ['ignore', 'pipe', 'pipe'] });
70
+ (0, child_process_1.execFileSync)('git', ['push', '--quiet', 'origin', branch], { cwd: dir, stdio: ['ignore', 'pipe', 'pipe'], timeout: 60_000 });
71
+ return { backend: 'git', branch, prUrl: gitCompareUrl(gitUrl, branch) };
72
+ }
73
+ finally {
74
+ fs_1.default.rmSync(dir, { recursive: true, force: true });
75
+ }
76
+ }
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.readUserFraimConfig = readUserFraimConfig;
7
7
  exports.writeUserFraimConfig = writeUserFraimConfig;
8
8
  exports.getOrganizationConfig = getOrganizationConfig;
9
+ exports.getManagerStorageConfig = getManagerStorageConfig;
9
10
  /**
10
11
  * Typed read/write helpers for the user-level FRAIM config (~/.fraim/config.json),
11
12
  * including the organization block introduced by issue #563.
@@ -66,3 +67,22 @@ function getOrganizationConfig() {
66
67
  }
67
68
  return null;
68
69
  }
70
+ /**
71
+ * Resolve the validated manager storage configuration, or null when no portable
72
+ * manager backend is configured on this machine.
73
+ */
74
+ function getManagerStorageConfig() {
75
+ const raw = readUserFraimConfig().managerStorage;
76
+ if (!raw || typeof raw !== 'object')
77
+ return null;
78
+ if (raw.backend === 'git') {
79
+ const gitUrl = typeof raw.gitUrl === 'string' ? raw.gitUrl.trim() : '';
80
+ if (!gitUrl)
81
+ return null;
82
+ return { backend: 'git', gitUrl, id: raw.id };
83
+ }
84
+ if (raw.backend === 'fraim-cloud') {
85
+ return { backend: 'fraim-cloud', id: raw.id };
86
+ }
87
+ return null;
88
+ }
@@ -33,7 +33,8 @@ function normalizeIntegrations(config) {
33
33
  itsm: current.itsm && typeof current.itsm === 'object'
34
34
  ? {
35
35
  provider: current.itsm.provider,
36
- instanceUrl: current.itsm.instanceUrl
36
+ instanceUrl: current.itsm.instanceUrl,
37
+ accessScript: current.itsm.accessScript
37
38
  }
38
39
  : undefined,
39
40
  identity: current.identity && typeof current.identity === 'object'
@@ -49,13 +50,16 @@ function normalizeAutomation(config) {
49
50
  support: {
50
51
  startMode: support.startMode,
51
52
  defaultDecisionMode: support.defaultDecisionMode,
52
- contextResolver: support.contextResolver && typeof support.contextResolver === 'object'
53
+ playbooks: support.playbooks && typeof support.playbooks === 'object'
53
54
  ? {
54
- scriptPath: support.contextResolver.scriptPath,
55
- arguments: Array.isArray(support.contextResolver.arguments) ? support.contextResolver.arguments : undefined,
56
- timeoutMs: support.contextResolver.timeoutMs
55
+ directory: support.playbooks.directory,
56
+ decisionCommand: support.playbooks.decisionCommand,
57
+ decisionTimeoutMs: support.playbooks.decisionTimeoutMs
57
58
  }
58
59
  : undefined,
60
+ actionAdapters: support.actionAdapters && typeof support.actionAdapters === 'object'
61
+ ? support.actionAdapters
62
+ : undefined,
59
63
  queue: support.queue && typeof support.queue === 'object'
60
64
  ? {
61
65
  provider: support.queue.provider,