fraim 2.0.318 → 2.0.320

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.
@@ -1,37 +1,4 @@
1
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
2
  var __importDefault = (this && this.__importDefault) || function (mod) {
36
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
37
4
  };
@@ -40,6 +7,9 @@ exports.firstRunCommand = exports.runFirstRun = void 0;
40
7
  const commander_1 = require("commander");
41
8
  const chalk_1 = __importDefault(require("chalk"));
42
9
  const child_process_1 = require("child_process");
10
+ const server_1 = require("../../first-run/server");
11
+ const session_service_1 = require("../../first-run/session-service");
12
+ const ports_1 = require("../../core/utils/ports");
43
13
  function openBrowser(url) {
44
14
  try {
45
15
  if (process.platform === 'win32') {
@@ -63,22 +33,17 @@ function openBrowser(url) {
63
33
  }
64
34
  }
65
35
  const runFirstRun = async (options) => {
66
- const [{ FirstRunServer }, { FirstRunSessionService }, { findAvailablePort }] = await Promise.all([
67
- Promise.resolve().then(() => __importStar(require('../../first-run/server'))),
68
- Promise.resolve().then(() => __importStar(require('../../first-run/session-service'))),
69
- Promise.resolve().then(() => __importStar(require('../../core/utils/ports'))),
70
- ]);
71
36
  // Issue #646: the key is optional. When launched by the no-terminal macOS
72
37
  // installer (.pkg), no key is passed — the wizard prompts the user to paste it.
73
38
  const key = options.key || process.env.FRAIM_API_KEY || process.env.FRAIM_SETUP_KEY || process.env.FRAIM_INSTALL_KEY || '';
74
- const sessionService = new FirstRunSessionService({
39
+ const sessionService = new session_service_1.FirstRunSessionService({
75
40
  key,
76
41
  headless: options.headless,
77
42
  resume: options.resume,
78
43
  projectRoot: options.projectRoot,
79
44
  });
80
- const server = new FirstRunServer({ sessionService });
81
- const port = await findAvailablePort(43120);
45
+ const server = new server_1.FirstRunServer({ sessionService });
46
+ const port = await (0, ports_1.findAvailablePort)(43120);
82
47
  const url = `http://127.0.0.1:${port}/first-run/`;
83
48
  await server.start(port);
84
49
  console.log(chalk_1.default.blue('Starting FRAIM first-run...'));
@@ -73,9 +73,23 @@ exports.getPortableManagedCommandPath = getPortableManagedCommandPath;
73
73
  const resolveManagedCommand = (command) => {
74
74
  if (!MANAGED_COMMANDS.has(command))
75
75
  return command;
76
- // Prefer a system-installed CLI so FRAIM doesn't install its own copy when
77
- // the machine already has one. Fall back to the FRAIM-managed portable copy
78
- // only when no system install is found. Last resort: bare command name.
79
- return (0, exports.getSystemCommandPath)(command) || (0, exports.getPortableManagedCommandPath)(command) || command;
76
+ // Prefer a system-installed CLI so FRAIM doesn't launch its own copy when
77
+ // the machine already has one. This process's own ambient PATH is checked
78
+ // first (cheap, no subprocess) but a Hub launched from a GUI/shortcut
79
+ // often does NOT inherit the user's full shell PATH, so a miss there does
80
+ // not yet mean "not installed". Issue: this exact gap let the ambient-PATH
81
+ // check silently fall through to FRAIM's portable copy — a separate,
82
+ // unauthenticated install — even when the user's real, logged-in system
83
+ // CLI was sitting right there, just outside this process's inherited PATH.
84
+ // The same location-agnostic recovery `fraim doctor` and the Settings
85
+ // "Check" button already rely on (issue #1256) closes that gap here too,
86
+ // so "is it on the system" can never again disagree between the launch
87
+ // path and the status-check path. Fall back to the FRAIM-managed portable
88
+ // copy only when no system install is found by either check. Last resort:
89
+ // bare command name.
90
+ return (0, exports.getSystemCommandPath)(command)
91
+ || (0, exports.getSystemCommandPath)(command, (0, managed_agent_paths_1.buildRecoveredSystemPath)(process.env.PATH))
92
+ || (0, exports.getPortableManagedCommandPath)(command)
93
+ || command;
80
94
  };
81
95
  exports.resolveManagedCommand = resolveManagedCommand;
@@ -138,6 +138,16 @@ function runtimeFromEnv(env = process.env) {
138
138
  ? { executable: env.FRAIM_PACKAGED_CLI_EXECUTABLE, script: env.FRAIM_PACKAGED_CLI_SCRIPT }
139
139
  : null;
140
140
  }
141
+ // Regular Node.js (and ELECTRON_RUN_AS_NODE) cannot see inside Electron's
142
+ // .asar archives. For paths like …/app.asar/node_modules/…, verify the
143
+ // archive file itself rather than the internal entry.
144
+ function existsOrAsarExists(filePath) {
145
+ const normalized = filePath.replace(/\\/g, '/');
146
+ const asarSep = normalized.indexOf('.asar/');
147
+ if (asarSep === -1)
148
+ return fs_1.default.existsSync(filePath);
149
+ return fs_1.default.existsSync(filePath.slice(0, asarSep + '.asar'.length));
150
+ }
141
151
  function getPackagedFraimRuntime(env = process.env) {
142
152
  const envRuntime = runtimeFromEnv(env);
143
153
  if (envRuntime)
@@ -146,7 +156,7 @@ function getPackagedFraimRuntime(env = process.env) {
146
156
  const raw = JSON.parse(fs_1.default.readFileSync(getPackagedFraimRuntimePath(), 'utf8'));
147
157
  if (!raw.executable || !raw.script)
148
158
  return null;
149
- if (!fs_1.default.existsSync(raw.executable) || !fs_1.default.existsSync(raw.script))
159
+ if (!fs_1.default.existsSync(raw.executable) || !existsOrAsarExists(raw.script))
150
160
  return null;
151
161
  return { executable: raw.executable, script: raw.script };
152
162
  }
@@ -17,6 +17,7 @@ exports.appendManagedAgentBinDirsToProcessPath = appendManagedAgentBinDirsToProc
17
17
  exports.__setLoginShellPathResolverForTests = __setLoginShellPathResolverForTests;
18
18
  exports.resolveLoginShellPathCached = resolveLoginShellPathCached;
19
19
  exports.__clearLoginShellPathCacheForTests = __clearLoginShellPathCacheForTests;
20
+ exports.buildRecoveredSystemPath = buildRecoveredSystemPath;
20
21
  exports.buildRecoveredAgentPath = buildRecoveredAgentPath;
21
22
  exports.versionFromProbeOutput = versionFromProbeOutput;
22
23
  const fs_1 = __importDefault(require("fs"));
@@ -261,7 +262,7 @@ let loginShellPathResolverOverride = null;
261
262
  function __setLoginShellPathResolverForTests(resolver) {
262
263
  loginShellPathResolverOverride = resolver;
263
264
  cachedLoginShellPath = undefined;
264
- cachedNpmGlobalBinDirsForRecovery = undefined;
265
+ cachedNpmGlobalBinDirsForRecovery.clear();
265
266
  }
266
267
  let cachedLoginShellPath;
267
268
  /**
@@ -288,7 +289,7 @@ function resolveLoginShellPathCached() {
288
289
  function __clearLoginShellPathCacheForTests() {
289
290
  cachedLoginShellPath = undefined;
290
291
  }
291
- let cachedNpmGlobalBinDirsForRecovery;
292
+ const cachedNpmGlobalBinDirsForRecovery = new Map();
292
293
  /**
293
294
  * Memoized wrapper around resolveNpmGlobalBinDirs(), scoped to buildRecoveredAgentPath()'s
294
295
  * own use only — NOT a change to resolveNpmGlobalBinDirs() itself, which fraim doctor and the
@@ -304,17 +305,28 @@ let cachedNpmGlobalBinDirsForRecovery;
304
305
  * blocking spawnSync is exactly the event-loop-freezing regression issue #1010 eliminated,
305
306
  * reintroduced one layer deeper. Memoizing once per process (like resolveLoginShellPathCached
306
307
  * above) restores the bound this repo's own #1010 postmortem requires.
308
+ *
309
+ * Keyed by `basePath` (not a single scalar): `resolveManagedCommand()` also routes through this
310
+ * cache now, and unlike `detectEmployeesAsync()`'s repeated same-ambient-PATH probing, its
311
+ * callers legitimately pass different constructed PATHs across calls in the same process (e.g.
312
+ * isolated tests that swap `FRAIM_USER_DIR`/`PATH` per test case). A single scalar cache would
313
+ * let the FIRST basePath's answer silently leak into every later, differently-scoped lookup —
314
+ * concretely, a real machine's actual npm-global `codex` shadowing a test's own isolated managed
315
+ * shim. Per-basePath keys keep the "once per distinct PATH" bound without that cross-contamination.
307
316
  */
308
317
  function resolveNpmGlobalBinDirsCached(basePath) {
309
- if (cachedNpmGlobalBinDirsForRecovery !== undefined)
310
- return cachedNpmGlobalBinDirsForRecovery;
318
+ const cached = cachedNpmGlobalBinDirsForRecovery.get(basePath);
319
+ if (cached !== undefined)
320
+ return cached;
321
+ let dirs;
311
322
  try {
312
- cachedNpmGlobalBinDirsForRecovery = resolveNpmGlobalBinDirs(basePath);
323
+ dirs = resolveNpmGlobalBinDirs(basePath);
313
324
  }
314
325
  catch {
315
- cachedNpmGlobalBinDirsForRecovery = [];
326
+ dirs = [];
316
327
  }
317
- return cachedNpmGlobalBinDirsForRecovery;
328
+ cachedNpmGlobalBinDirsForRecovery.set(basePath, dirs);
329
+ return dirs;
318
330
  }
319
331
  /**
320
332
  * Issue #1256 (AC-A3): the single shared PATH-recovery helper every agent-resolution call
@@ -330,7 +342,15 @@ function resolveNpmGlobalBinDirsCached(basePath) {
330
342
  * in npm-global bin dirs resolved against that recovered PATH (covers the npm case even where
331
343
  * a login shell is unavailable) → append FRAIM's managed agent bins last, unconditionally.
332
344
  */
333
- function buildRecoveredAgentPath(basePath) {
345
+ // Same recovery as `buildRecoveredAgentPath()` but stops short of appending
346
+ // FRAIM's own managed agent bin dirs (including the legacy flat directory —
347
+ // see `getPortableManagedCommandPath()`'s versioned-dir-only guard against
348
+ // Defect A). Callers that need to distinguish "found on the real machine"
349
+ // from "fell back to FRAIM's bundled copy" — i.e. `resolveManagedCommand()`
350
+ // — resolve against this path for the system-install check, then consult
351
+ // `getPortableManagedCommandPath()` (never the flat dir) as an explicit,
352
+ // separate fallback tier.
353
+ function buildRecoveredSystemPath(basePath) {
334
354
  const withoutManaged = stripManagedAgentBinDirsFromPath(basePath);
335
355
  const withoutProjectBins = stripProjectLocalNodeBinDirs(withoutManaged);
336
356
  const loginShellPath = resolveLoginShellPathCached();
@@ -340,8 +360,10 @@ function buildRecoveredAgentPath(basePath) {
340
360
  : withoutProjectBins;
341
361
  const withLoginShellClean = stripProjectLocalNodeBinDirs(withLoginShell);
342
362
  const npmGlobalBinDirs = resolveNpmGlobalBinDirsCached(withLoginShellClean);
343
- const withNpmGlobal = appendBinDirsToPath(withLoginShellClean, npmGlobalBinDirs);
344
- return appendBinDirsToPath(withNpmGlobal, getManagedAgentBinDirs());
363
+ return appendBinDirsToPath(withLoginShellClean, npmGlobalBinDirs);
364
+ }
365
+ function buildRecoveredAgentPath(basePath) {
366
+ return appendBinDirsToPath(buildRecoveredSystemPath(basePath), getManagedAgentBinDirs());
345
367
  }
346
368
  // ─── Issue #1256 (slice d): trust stdout, not stderr, for a version answer ───
347
369
  //
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fraim",
3
- "version": "2.0.318",
3
+ "version": "2.0.320",
4
4
  "description": "FRAIM core CLI and MCP package.",
5
5
  "main": "index.js",
6
6
  "bin": {