fraim 2.0.202 → 2.0.204

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 -11024
  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,52 +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.getLatestPublishedVersion = getLatestPublishedVersion;
7
- exports.__resetLatestVersionCache = __resetLatestVersionCache;
8
- const https_1 = __importDefault(require("https"));
9
- // Issue #755: best-effort "latest published fraim version" for the Hub's
10
- // update-available nudge. Cached in-process (1h TTL) and never throws — returns
11
- // null when offline or the registry is unreachable, so the nudge simply hides.
12
- const TTL_MS = 60 * 60 * 1000;
13
- const REGISTRY_URL = 'https://registry.npmjs.org/fraim/latest';
14
- let cache = { value: null, at: 0 };
15
- async function getLatestPublishedVersion() {
16
- const now = Date.now();
17
- if (cache.at !== 0 && now - cache.at < TTL_MS)
18
- return cache.value;
19
- const value = await fetchNpmLatest();
20
- // Only cache successful lookups; a failed fetch retries next call rather than
21
- // caching a null for an hour.
22
- if (value !== null)
23
- cache = { value, at: now };
24
- return value ?? cache.value;
25
- }
26
- /** Test-only: reset the module cache. */
27
- function __resetLatestVersionCache() {
28
- cache = { value: null, at: 0 };
29
- }
30
- function fetchNpmLatest() {
31
- return new Promise((resolve) => {
32
- const req = https_1.default.get(REGISTRY_URL, { timeout: 4000 }, (res) => {
33
- if (res.statusCode !== 200) {
34
- res.resume();
35
- return resolve(null);
36
- }
37
- let body = '';
38
- res.on('data', (chunk) => { body += chunk; });
39
- res.on('end', () => {
40
- try {
41
- const version = JSON.parse(body).version;
42
- resolve(typeof version === 'string' ? version : null);
43
- }
44
- catch {
45
- resolve(null);
46
- }
47
- });
48
- });
49
- req.on('error', () => resolve(null));
50
- req.on('timeout', () => { req.destroy(); resolve(null); });
51
- });
52
- }
@@ -1,57 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.decideHubLaunch = decideHubLaunch;
37
- const semver = __importStar(require("semver"));
38
- function decideHubLaunch(input) {
39
- const { running, pidAlive, cliVersion, restart, noRestart } = input;
40
- if (!running || !pidAlive) {
41
- return { action: 'launch-fresh', reason: 'no live Hub instance is running' };
42
- }
43
- if (noRestart) {
44
- return { action: 'focus-existing', reason: '--no-restart: keeping the running instance' };
45
- }
46
- if (restart) {
47
- return { action: 'replace', reason: '--restart: replacing the running instance' };
48
- }
49
- // Only treat as stale when BOTH versions are valid semver and the running one is
50
- // strictly older. On any uncertainty (unknown/unparseable version) do not kill —
51
- // conservative default is to focus the existing instance.
52
- const comparable = !!semver.valid(running.version) && !!semver.valid(cliVersion);
53
- if (comparable && semver.lt(running.version, cliVersion)) {
54
- return { action: 'replace', reason: `running ${running.version} is older than ${cliVersion}; replacing with the newer build` };
55
- }
56
- return { action: 'focus-existing', reason: `running ${running.version} is current or not comparable to ${cliVersion}` };
57
- }
@@ -1,43 +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.hubRuntimeFilePath = hubRuntimeFilePath;
7
- exports.writeHubRuntimeFile = writeHubRuntimeFile;
8
- exports.readHubRuntimeFile = readHubRuntimeFile;
9
- exports.removeHubRuntimeFile = removeHubRuntimeFile;
10
- const fs_1 = __importDefault(require("fs"));
11
- const path_1 = __importDefault(require("path"));
12
- // Issue #755: the running desktop Hub writes ~/.fraim/hub-runtime.json so the
13
- // `fraim hub` CLI can discover a live instance (pid/port/version) before deciding
14
- // whether to replace a stale build. `fraimDir` is injected (never hard-coded to
15
- // the real ~/.fraim) so tests isolate via a temp directory.
16
- const RUNTIME_FILE = 'hub-runtime.json';
17
- function hubRuntimeFilePath(fraimDir) {
18
- return path_1.default.join(fraimDir, RUNTIME_FILE);
19
- }
20
- function writeHubRuntimeFile(fraimDir, info) {
21
- fs_1.default.mkdirSync(fraimDir, { recursive: true });
22
- fs_1.default.writeFileSync(hubRuntimeFilePath(fraimDir), JSON.stringify(info, null, 2));
23
- }
24
- function readHubRuntimeFile(fraimDir) {
25
- try {
26
- const parsed = JSON.parse(fs_1.default.readFileSync(hubRuntimeFilePath(fraimDir), 'utf8'));
27
- if (typeof parsed.pid !== 'number' || typeof parsed.port !== 'number' || typeof parsed.version !== 'string') {
28
- return null;
29
- }
30
- return { pid: parsed.pid, port: parsed.port, version: parsed.version, startedAt: String(parsed.startedAt ?? '') };
31
- }
32
- catch {
33
- return null;
34
- }
35
- }
36
- function removeHubRuntimeFile(fraimDir) {
37
- try {
38
- fs_1.default.rmSync(hubRuntimeFilePath(fraimDir), { force: true });
39
- }
40
- catch {
41
- /* no-op: absent file is fine */
42
- }
43
- }
@@ -1,269 +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.ManagedBrowser = void 0;
7
- exports.browserExecutableCandidates = browserExecutableCandidates;
8
- exports.resolveBrowserExecutable = resolveBrowserExecutable;
9
- exports.buildBrowserLaunchArgs = buildBrowserLaunchArgs;
10
- exports.buildBrowserContextNote = buildBrowserContextNote;
11
- exports.defaultBrowserProfileDir = defaultBrowserProfileDir;
12
- exports.probeCdpEndpoint = probeCdpEndpoint;
13
- /**
14
- * #521 — FRAIM-managed persistent browser.
15
- *
16
- * Problem: the Hub runs each agent turn as a one-shot process (`claude -p …`).
17
- * When a turn ends, that process exits and any browser it launched (a child of
18
- * the agent) is torn down — so "open a browser, I'll log in, then continue"
19
- * never works: the window dies at the turn boundary before the manager can log in.
20
- *
21
- * Fix: FRAIM owns ONE long-lived Chromium browser (Chrome or Edge) launched with
22
- * a remote-debugging port and a persistent profile. It outlives agent turns.
23
- * Agents `connectOverCDP` to it instead of launching their own, so the session —
24
- * including a manual login — survives across turns. One shared browser, one
25
- * persistent profile; concurrency isolation is intentionally NOT solved here
26
- * (see persistent-browser-session skill) to keep this simple.
27
- */
28
- const child_process_1 = require("child_process");
29
- const http_1 = __importDefault(require("http"));
30
- const os_1 = __importDefault(require("os"));
31
- const path_1 = __importDefault(require("path"));
32
- const fs_1 = __importDefault(require("fs"));
33
- /**
34
- * Known install locations for Chrome and Edge per platform. Pure + injectable so
35
- * resolution can be unit-tested without the browsers actually being installed.
36
- */
37
- function browserExecutableCandidates(platform = process.platform, env = process.env) {
38
- if (platform === 'win32') {
39
- const pf = env['ProgramFiles'] || 'C:\\Program Files';
40
- const pf86 = env['ProgramFiles(x86)'] || 'C:\\Program Files (x86)';
41
- const local = env['LOCALAPPDATA']
42
- || path_1.default.join(env['USERPROFILE'] || 'C:\\Users\\Default', 'AppData', 'Local');
43
- return {
44
- chrome: [
45
- path_1.default.join(pf, 'Google', 'Chrome', 'Application', 'chrome.exe'),
46
- path_1.default.join(pf86, 'Google', 'Chrome', 'Application', 'chrome.exe'),
47
- path_1.default.join(local, 'Google', 'Chrome', 'Application', 'chrome.exe'),
48
- ],
49
- msedge: [
50
- path_1.default.join(pf86, 'Microsoft', 'Edge', 'Application', 'msedge.exe'),
51
- path_1.default.join(pf, 'Microsoft', 'Edge', 'Application', 'msedge.exe'),
52
- ],
53
- };
54
- }
55
- if (platform === 'darwin') {
56
- return {
57
- chrome: ['/Applications/Google Chrome.app/Contents/MacOS/Google Chrome'],
58
- msedge: ['/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge'],
59
- };
60
- }
61
- // linux + anything else
62
- return {
63
- chrome: ['/usr/bin/google-chrome', '/usr/bin/google-chrome-stable', '/usr/bin/chromium', '/usr/bin/chromium-browser'],
64
- msedge: ['/usr/bin/microsoft-edge', '/usr/bin/microsoft-edge-stable'],
65
- };
66
- }
67
- /**
68
- * Resolve a Chromium executable. Returns null when neither Chrome nor Edge is
69
- * found (caller surfaces a clear "install Chrome or Edge" error).
70
- */
71
- function resolveBrowserExecutable(opts = {}) {
72
- const exists = opts.exists || ((p) => fs_1.default.existsSync(p));
73
- if (opts.explicitPath) {
74
- if (exists(opts.explicitPath)) {
75
- const channel = /edge|msedge/i.test(opts.explicitPath) ? 'msedge' : 'chrome';
76
- return { channel, path: opts.explicitPath };
77
- }
78
- return null;
79
- }
80
- const candidates = browserExecutableCandidates(opts.platform, opts.env);
81
- const order = opts.channel === 'msedge'
82
- ? ['msedge', 'chrome']
83
- : ['chrome', 'msedge']; // 'chrome' and 'auto' both prefer Chrome first
84
- for (const channel of order) {
85
- for (const candidate of candidates[channel]) {
86
- if (exists(candidate))
87
- return { channel, path: candidate };
88
- }
89
- }
90
- return null;
91
- }
92
- /**
93
- * Args that put Chrome/Edge into a debuggable, prompt-free, persistent-profile
94
- * state. Headed (no --headless) so the manager can log in by hand.
95
- */
96
- function buildBrowserLaunchArgs(opts) {
97
- return [
98
- `--remote-debugging-port=${opts.port}`,
99
- `--user-data-dir=${opts.userDataDir}`,
100
- '--no-first-run',
101
- '--no-default-browser-check',
102
- // Required so Playwright's CDP WebSocket handshake is accepted by recent Chromium.
103
- '--remote-allow-origins=*',
104
- 'about:blank',
105
- ];
106
- }
107
- /**
108
- * The runtime guidance the Hub injects into an agent's start message so it uses
109
- * the shared browser. This is intentionally a UI/Hub-layer injection — it is NOT
110
- * baked into any registry job or skill. Returns '' when the shared browser is not
111
- * available (so the note only appears when there is something to connect to).
112
- */
113
- function buildBrowserContextNote(cdpEndpoint, hubBaseUrl) {
114
- if (!cdpEndpoint || !hubBaseUrl)
115
- return '';
116
- return [
117
- '',
118
- '[FRAIM shared browser] A persistent shared browser is available — use it instead of launching your own.',
119
- `Before using a browser, ensure it is running: \`POST ${hubBaseUrl}/api/ai-hub/browser/start\` (idempotent — it reuses the running browser, or relaunches it if it was closed).`,
120
- `Then drive the shared browser at ${cdpEndpoint}: your Playwright/browser tools are pointed at it on supported agents; if your tools would otherwise open their own browser, connect to the shared one with \`connectOverCDP("${cdpEndpoint}")\` and reuse its existing context/tab.`,
121
- 'Never close the shared browser or sign me out. If a site needs login, open it there and ask me to log in — the session persists to your next turn.',
122
- ].join('\n');
123
- }
124
- /** Default persistent profile directory — sandboxed from the user's daily browser. */
125
- function defaultBrowserProfileDir(env = process.env) {
126
- const home = env['FRAIM_HOME'] || os_1.default.homedir();
127
- return path_1.default.join(home, '.fraim', 'browser-profile');
128
- }
129
- /**
130
- * Probe a CDP endpoint's /json/version. Resolves true only when it answers with a
131
- * Chromium DevTools payload. Uses node http (no fetch dependency).
132
- */
133
- function probeCdpEndpoint(endpoint, timeoutMs = 1500) {
134
- return new Promise((resolve) => {
135
- let url;
136
- try {
137
- url = new URL('/json/version', endpoint);
138
- }
139
- catch {
140
- resolve(false);
141
- return;
142
- }
143
- const req = http_1.default.get(url, { timeout: timeoutMs }, (res) => {
144
- let body = '';
145
- res.on('data', (chunk) => { body += chunk.toString(); });
146
- res.on('end', () => {
147
- try {
148
- const data = JSON.parse(body);
149
- resolve(!!(data && (data.webSocketDebuggerUrl || data.Browser)));
150
- }
151
- catch {
152
- resolve(false);
153
- }
154
- });
155
- });
156
- req.on('error', () => resolve(false));
157
- req.on('timeout', () => { req.destroy(); resolve(false); });
158
- });
159
- }
160
- const DEFAULT_PORT = 9222;
161
- /**
162
- * Owns the lifecycle of the single shared browser. start() is idempotent — it
163
- * reuses an already-running browser on the port — so multiple callers (and Hub
164
- * restarts) converge on one instance.
165
- */
166
- class ManagedBrowser {
167
- constructor(opts = {}) {
168
- this.child = null;
169
- this.ownsProcess = false;
170
- this.resolved = null;
171
- this.opts = opts;
172
- this.channel = opts.channel || 'auto';
173
- this.port = opts.port || DEFAULT_PORT;
174
- this.host = opts.host || '127.0.0.1';
175
- this.userDataDir = opts.userDataDir || defaultBrowserProfileDir(opts.env);
176
- }
177
- cdpEndpoint() {
178
- return `http://${this.host}:${this.port}`;
179
- }
180
- probe() {
181
- return this.opts.probeFn ? this.opts.probeFn(this.cdpEndpoint()) : probeCdpEndpoint(this.cdpEndpoint());
182
- }
183
- /** Live check — does a Chromium CDP endpoint answer on the port right now. */
184
- isRunning() {
185
- return this.probe();
186
- }
187
- async start() {
188
- // 1. Reuse a browser already listening on the port (idempotent across callers
189
- // and Hub restarts; also lets the manager pre-launch their own).
190
- if (await this.probe()) {
191
- return { endpoint: this.cdpEndpoint(), reused: true, executable: this.resolved?.path || null, channel: this.resolved?.channel || null };
192
- }
193
- // Already spawned by us but not yet reachable → just wait for it.
194
- if (this.child && this.ownsProcess) {
195
- await this.waitForRunning();
196
- return { endpoint: this.cdpEndpoint(), reused: false, executable: this.resolved?.path || null, channel: this.resolved?.channel || null };
197
- }
198
- // 2. Resolve the executable.
199
- const exe = resolveBrowserExecutable({
200
- channel: this.channel,
201
- explicitPath: this.opts.explicitPath,
202
- platform: this.opts.platform,
203
- env: this.opts.env,
204
- exists: this.opts.exists,
205
- });
206
- if (!exe) {
207
- throw new Error('No Chrome or Edge found. Install Google Chrome or Microsoft Edge, or set FRAIM_BROWSER_PATH to a Chromium executable.');
208
- }
209
- this.resolved = exe;
210
- // 3. Ensure the profile dir exists, then spawn the browser long-lived. It is a
211
- // child of the Hub server (so it dies with the Hub) but NOT of any agent
212
- // turn — that is the whole point.
213
- try {
214
- if (!this.opts.exists)
215
- fs_1.default.mkdirSync(this.userDataDir, { recursive: true });
216
- }
217
- catch { /* best-effort; browser will create it */ }
218
- const args = buildBrowserLaunchArgs({ port: this.port, userDataDir: this.userDataDir });
219
- const spawnFn = this.opts.spawnFn || ((c, a, o) => (0, child_process_1.spawn)(c, a, o));
220
- const child = spawnFn(exe.path, args, { stdio: 'ignore', windowsHide: false });
221
- this.child = child;
222
- this.ownsProcess = true;
223
- if (typeof child.on === 'function') {
224
- child.on('exit', () => {
225
- if (this.child === child) {
226
- this.child = null;
227
- this.ownsProcess = false;
228
- }
229
- });
230
- }
231
- // 4. Wait until the CDP endpoint is reachable before returning the endpoint.
232
- await this.waitForRunning();
233
- return { endpoint: this.cdpEndpoint(), reused: false, executable: exe.path, channel: exe.channel };
234
- }
235
- async waitForRunning() {
236
- const timeoutMs = this.opts.waitTimeoutMs ?? 15000;
237
- const intervalMs = this.opts.waitIntervalMs ?? 250;
238
- const startedAt = Date.now();
239
- for (;;) {
240
- if (await this.probe())
241
- return;
242
- if (Date.now() - startedAt > timeoutMs) {
243
- throw new Error(`Browser did not become reachable on ${this.cdpEndpoint()} within ${timeoutMs}ms.`);
244
- }
245
- await new Promise((r) => setTimeout(r, intervalMs));
246
- }
247
- }
248
- /** Kill the browser only if WE launched it — never a browser the manager owns. */
249
- stop() {
250
- if (this.child && this.ownsProcess && typeof this.child.kill === 'function') {
251
- try {
252
- this.child.kill();
253
- }
254
- catch { /* ignore */ }
255
- }
256
- this.child = null;
257
- this.ownsProcess = false;
258
- }
259
- status() {
260
- return {
261
- ownsProcess: this.ownsProcess,
262
- endpoint: this.cdpEndpoint(),
263
- channel: this.resolved?.channel || this.channel,
264
- userDataDir: this.userDataDir,
265
- executable: this.resolved?.path || null,
266
- };
267
- }
268
- }
269
- exports.ManagedBrowser = ManagedBrowser;
@@ -1,64 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.extractExplicitFraimInvocation = extractExplicitFraimInvocation;
4
- exports.fraimInvocationFor = fraimInvocationFor;
5
- exports.buildCommunicationStyleNote = buildCommunicationStyleNote;
6
- exports.buildSameJobContinueMessage = buildSameJobContinueMessage;
7
- exports.buildManagerMessage = buildManagerMessage;
8
- function extractExplicitFraimInvocation(text) {
9
- const raw = String(text || '');
10
- const match = raw.match(/(?:^|\n|\s)([$/@]fraim)\s+([a-z0-9][a-z0-9-]*)(?=\s|$)/i);
11
- if (!match || match.index == null)
12
- return null;
13
- const before = raw.slice(0, match.index).trim();
14
- const after = raw.slice(match.index + match[0].length).trim();
15
- const remainder = [before, after].filter(Boolean).join('\n\n').trim();
16
- return {
17
- symbol: match[1].toLowerCase(),
18
- jobId: match[2].toLowerCase(),
19
- remainder,
20
- };
21
- }
22
- function fraimInvocationFor(employeeId, jobId) {
23
- if (jobId === '__freeform__')
24
- return null;
25
- const symbol = employeeId === 'codex' ? '$fraim' : '/fraim';
26
- return `${symbol} ${jobId}`;
27
- }
28
- // #521: a Hub-injected communication-style note so the employee's messages to the
29
- // manager stay about the JOB and its outcomes — not the FRAIM machinery (seeking
30
- // mentoring, tool calls, git, phase switches). The structured signals (the
31
- // tracker's seekMentoring events, the review_handoff) still flow as tool calls
32
- // behind the scenes, so this only shapes the human-facing prose. Injected at the
33
- // Hub layer — it never edits the jobs.
34
- function buildCommunicationStyleNote() {
35
- return [
36
- '',
37
- '[How to talk to me] In your messages to me, report ONLY on the job and its outcome — what you found, what you changed, the decisions you made, blockers, and what you need from me. Do NOT narrate the FRAIM machinery: don\'t announce that you are talking to FRAIM, asking your mentor, following the process, moving between phases, or calling tools (git, playwright, etc.). I can see the raw tool activity separately if I want it. Keep your updates short and about the work, not the process.',
38
- ].join('\n');
39
- }
40
- // Issue #732: a plain continue of the SAME active job must not re-load the job.
41
- // Continue turns resume the existing agent session, which already has the job
42
- // loaded, so this message carries NO `/fraim <job>` invocation — the headless
43
- // transform therefore does not re-instruct get_fraim_job (which is slow and
44
- // mints a fresh UUID job id every call). The manager coaching still reaches the
45
- // agent, which advances the workflow via seekMentoring using its resumed context.
46
- function buildSameJobContinueMessage(coaching) {
47
- const remainder = String(coaching || '').trim();
48
- return remainder
49
- ? `Continue the active FRAIM job with this manager coaching:\n\n${remainder}`
50
- : 'Continue the active FRAIM job using the current session context.';
51
- }
52
- function buildManagerMessage(employeeId, jobId, kind, instructions, stubPath) {
53
- const trimmed = String(instructions || '').trim();
54
- const explicit = extractExplicitFraimInvocation(trimmed);
55
- const effectiveJobId = explicit?.jobId || jobId;
56
- const invocation = fraimInvocationFor(employeeId, effectiveJobId);
57
- if (!invocation)
58
- return explicit?.remainder || trimmed;
59
- const remainder = explicit ? explicit.remainder : trimmed;
60
- const stub = (kind === 'start' && stubPath) ? `\n[Job stub: ${stubPath}]` : '';
61
- if (!remainder)
62
- return `${invocation}${stub}`;
63
- return `${invocation}${stub}\n\n${remainder}`;
64
- }
@@ -1,156 +0,0 @@
1
- "use strict";
2
- /**
3
- * Sideloads Office add-in manifests (Word + PowerPoint) so they appear under
4
- * Insert > My Add-ins > Developer Add-ins without admin rights or AppSource.
5
- *
6
- * Windows: writes HKCU\SOFTWARE\Microsoft\Office\16.0\WEF\Developer\<guid> =
7
- * ABSOLUTE FILE PATH to manifest.xml. This is exactly what Microsoft's
8
- * `office-addin-dev-settings register` writes. A URL value is for
9
- * SharePoint/network-share catalogs and yields "catalog access denied"
10
- * for a developer sideload — do NOT use a URL here.
11
- * macOS: copies the manifest into each app's
12
- * ~/Library/Containers/<bundle>/Data/Documents/wef/<guid>.xml
13
- *
14
- * NOTE: the manifest XML itself must be ASCII-clean (no smart quotes, em/en
15
- * dashes, or BOM) or Office's parser hard-fails with -1072894428 and the pane
16
- * silently never appears. See project_rules.md.
17
- *
18
- * Both paths are non-admin and survive app updates (keyed by manifest GUID).
19
- * Safe to call multiple times — idempotent.
20
- */
21
- var __importDefault = (this && this.__importDefault) || function (mod) {
22
- return (mod && mod.__esModule) ? mod : { "default": mod };
23
- };
24
- Object.defineProperty(exports, "__esModule", { value: true });
25
- exports.manifestXmlForPort = manifestXmlForPort;
26
- exports.isSideloaded = isSideloaded;
27
- exports.sideloadManifest = sideloadManifest;
28
- exports.removeSideload = removeSideload;
29
- const fs_1 = __importDefault(require("fs"));
30
- const path_1 = __importDefault(require("path"));
31
- const os_1 = __importDefault(require("os"));
32
- const child_process_1 = require("child_process");
33
- const WEF_DEVELOPER_KEY = 'HKCU\\SOFTWARE\\Microsoft\\Office\\16.0\\WEF\\Developer';
34
- const MANIFESTS = [
35
- {
36
- guid: 'd1090951-50cf-4cf2-9d12-b0f8541d265c',
37
- candidates: (base) => [
38
- path_1.default.resolve(base, 'extensions/office-word/manifest.xml'),
39
- path_1.default.resolve(__dirname, '..', '..', 'extensions/office-word/manifest.xml'),
40
- path_1.default.resolve(__dirname, '..', '..', '..', 'extensions/office-word/manifest.xml'),
41
- ],
42
- macContainer: 'com.microsoft.Word',
43
- },
44
- {
45
- guid: 'e7a3c812-91fd-4b2e-8c15-d4f6a903b71e',
46
- candidates: (base) => [
47
- path_1.default.resolve(base, 'public/ai-hub/powerpoint-taskpane/manifest.xml'),
48
- path_1.default.resolve(__dirname, '..', '..', 'public/ai-hub/powerpoint-taskpane/manifest.xml'),
49
- path_1.default.resolve(__dirname, '..', '..', '..', 'public/ai-hub/powerpoint-taskpane/manifest.xml'),
50
- ],
51
- macContainer: 'com.microsoft.Powerpoint',
52
- },
53
- ];
54
- function resolveManifestPath(entry, projectPath) {
55
- return entry.candidates(projectPath).find(c => fs_1.default.existsSync(c)) ?? null;
56
- }
57
- function macWefPath(container, guid) {
58
- return path_1.default.join(os_1.default.homedir(), 'Library', 'Containers', container, 'Data', 'Documents', 'wef', `${guid}.xml`);
59
- }
60
- function generatedManifestPath(entry, userDataDir) {
61
- return path_1.default.join(userDataDir, 'office-manifests', entry.guid, 'manifest.xml');
62
- }
63
- function manifestXmlForPort(xml, httpPort) {
64
- return xml.replace(/(https?:\/\/(?:localhost|127\.0\.0\.1)):43091/g, `$1:${httpPort}`);
65
- }
66
- function prepareManifestForSideload(entry, sourcePath, options) {
67
- if (!options.httpPort)
68
- return sourcePath;
69
- const userDataDir = options.userDataDir || path_1.default.join(os_1.default.homedir(), '.fraim');
70
- const dest = generatedManifestPath(entry, userDataDir);
71
- const rendered = manifestXmlForPort(fs_1.default.readFileSync(sourcePath, 'utf8'), options.httpPort);
72
- fs_1.default.mkdirSync(path_1.default.dirname(dest), { recursive: true });
73
- fs_1.default.writeFileSync(dest, rendered, { encoding: 'utf8' });
74
- return dest;
75
- }
76
- // ---------------------------------------------------------------------------
77
- // Windows registry helpers
78
- // ---------------------------------------------------------------------------
79
- function winRegisteredValue(guid) {
80
- const r = (0, child_process_1.spawnSync)('reg', ['query', WEF_DEVELOPER_KEY, '/v', guid], { encoding: 'utf8' });
81
- if (r.status !== 0 || !r.stdout.includes('REG_SZ'))
82
- return null;
83
- // Output line looks like: " <guid> REG_SZ C:\path\to\manifest.xml"
84
- const line = r.stdout.split(/\r?\n/).find(l => l.includes('REG_SZ'));
85
- if (!line)
86
- return null;
87
- const idx = line.indexOf('REG_SZ');
88
- return line.slice(idx + 'REG_SZ'.length).trim() || null;
89
- }
90
- // ---------------------------------------------------------------------------
91
- // Public API
92
- // ---------------------------------------------------------------------------
93
- /** Returns true when ALL manifests are sideloaded with the correct value. */
94
- function isSideloaded() {
95
- return MANIFESTS.every(m => isManifestSideloaded(m));
96
- }
97
- function isManifestSideloaded(m) {
98
- if (process.platform === 'win32') {
99
- const value = winRegisteredValue(m.guid);
100
- // Must be a real file path that exists — a URL or a stale/missing path
101
- // means we have NOT correctly sideloaded and should re-register.
102
- return !!value && !/^https?:\/\//i.test(value) && fs_1.default.existsSync(value);
103
- }
104
- if (process.platform === 'darwin')
105
- return fs_1.default.existsSync(macWefPath(m.macContainer, m.guid));
106
- return false;
107
- }
108
- /**
109
- * Sideloads all manifests. Returns ok:true if every manifest was registered.
110
- * Returns ok:false with the first failure reason.
111
- */
112
- function sideloadManifest(projectPath, options = {}) {
113
- for (const entry of MANIFESTS) {
114
- const manifestPath = resolveManifestPath(entry, projectPath);
115
- if (!manifestPath) {
116
- return { ok: false, reason: `Manifest not found for GUID ${entry.guid} — check extensions/office-word/ and public/ai-hub/powerpoint-taskpane/` };
117
- }
118
- const sideloadPath = prepareManifestForSideload(entry, manifestPath, options);
119
- if (process.platform === 'win32') {
120
- // Developer-key value = absolute file path to manifest.xml (NOT a URL).
121
- const r = (0, child_process_1.spawnSync)('reg', [
122
- 'add', WEF_DEVELOPER_KEY,
123
- '/v', entry.guid, '/t', 'REG_SZ', '/d', sideloadPath, '/f',
124
- ], { encoding: 'utf8' });
125
- if (r.status !== 0)
126
- return { ok: false, reason: r.stderr || `reg add failed for ${entry.guid}` };
127
- continue;
128
- }
129
- if (process.platform === 'darwin') {
130
- const dest = macWefPath(entry.macContainer, entry.guid);
131
- try {
132
- fs_1.default.mkdirSync(path_1.default.dirname(dest), { recursive: true });
133
- fs_1.default.copyFileSync(sideloadPath, dest);
134
- }
135
- catch (err) {
136
- return { ok: false, reason: String(err) };
137
- }
138
- continue;
139
- }
140
- return { ok: false, reason: `Unsupported platform: ${process.platform}` };
141
- }
142
- return { ok: true };
143
- }
144
- /** Removes all sideloaded manifests. */
145
- function removeSideload() {
146
- for (const entry of MANIFESTS) {
147
- if (process.platform === 'win32') {
148
- (0, child_process_1.spawnSync)('reg', ['delete', WEF_DEVELOPER_KEY, '/v', entry.guid, '/f'], { encoding: 'utf8' });
149
- }
150
- else if (process.platform === 'darwin') {
151
- const target = macWefPath(entry.macContainer, entry.guid);
152
- if (fs_1.default.existsSync(target))
153
- fs_1.default.unlinkSync(target);
154
- }
155
- }
156
- }