@companion-ai/feynman 0.2.60 → 0.2.61

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.
Binary file
package/RELEASES.md CHANGED
@@ -4,6 +4,20 @@ This file is the public release history for Feynman. Keep entries user-facing: w
4
4
 
5
5
  GitHub release notes are generated from the matching `## vX.Y.Z` section in this file.
6
6
 
7
+ ## v0.2.61 - 2026-06-11
8
+
9
+ ### Windows
10
+
11
+ - Fixed bundled-package setup failing on every launch (#177, #170). Two root causes found by running the published package on real Windows runners: GNU tar (Git for Windows) treats the workspace archive's absolute `C:\...` path as a remote host spec ("Cannot connect ... resolve failed"), and the npm fallback spawned bare `npm` without a shell, which Windows rejects with EINVAL. The archive now extracts with relative paths, and npm is invoked through `npm-cli.js` with the running Node executable.
12
+
13
+ ### Runtime Reliability
14
+
15
+ - The bundled workspace's alpha-hub copy now receives the same launch-time patches as the package-local copy, so the #167 search fix applies regardless of which copy resolves.
16
+
17
+ ### Validation
18
+
19
+ - The multi-OS end-to-end workflow now verifies install, update, patch application, and live model + subagent smokes on Windows, Linux, and macOS at Node 24 and 25.
20
+
7
21
  ## v0.2.60 - 2026-06-11
8
22
 
9
23
  ### Node Support
@@ -3,7 +3,9 @@ import { cpSync, existsSync, lstatSync, mkdirSync, readdirSync, readFileSync, re
3
3
  import { fileURLToPath } from "node:url";
4
4
  import { dirname, isAbsolute, join, relative, resolve } from "node:path";
5
5
  import { DefaultPackageManager, SettingsManager } from "@earendil-works/pi-coding-agent";
6
+ import { resolveAdjacentNpmCommand } from "../../scripts/lib/npm-command.mjs";
6
7
  import { NATIVE_PACKAGE_SOURCES, supportsNativePackageSources } from "./package-presets.js";
8
+ export { resolveAdjacentNpmCommand };
7
9
  import { applyFeynmanPackageManagerEnv, getFeynmanNpmPrefixPath } from "./runtime.js";
8
10
  import { getPathWithCurrentNode, resolveExecutable } from "../system/executables.js";
9
11
  const FILTERED_INSTALL_OUTPUT_PATTERNS = [
@@ -160,19 +162,6 @@ function ensureProjectInstallRoot(workingDir) {
160
162
  }
161
163
  return installRoot;
162
164
  }
163
- export function resolveAdjacentNpmCommand(nodeExecutablePath = process.execPath, platform = process.platform) {
164
- const executableDir = dirname(nodeExecutablePath);
165
- if (platform === "win32") {
166
- const npmCliPath = resolve(executableDir, "node_modules", "npm", "bin", "npm-cli.js");
167
- if (existsSync(npmCliPath)) {
168
- return { command: nodeExecutablePath, args: [npmCliPath] };
169
- }
170
- const npmCmdPath = resolve(executableDir, "npm.cmd");
171
- return existsSync(npmCmdPath) ? { command: npmCmdPath, args: [], shell: true } : undefined;
172
- }
173
- const candidate = resolve(executableDir, "npm");
174
- return existsSync(candidate) ? { command: candidate, args: [] } : undefined;
175
- }
176
165
  function resolvePackageManagerCommand(settingsManager) {
177
166
  const configured = settingsManager.getNpmCommand();
178
167
  if (!configured || configured.length === 0) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@companion-ai/feynman",
3
- "version": "0.2.60",
3
+ "version": "0.2.61",
4
4
  "description": "Research-first CLI agent built on Pi and alphaXiv",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -0,0 +1,10 @@
1
+ export type PackageManagerCommand = {
2
+ command: string;
3
+ args: string[];
4
+ shell?: boolean;
5
+ };
6
+
7
+ export declare function resolveAdjacentNpmCommand(
8
+ nodeExecutablePath?: string,
9
+ platform?: NodeJS.Platform,
10
+ ): PackageManagerCommand | undefined;
@@ -0,0 +1,21 @@
1
+ import { existsSync } from "node:fs";
2
+ import { dirname, resolve } from "node:path";
3
+
4
+ // Windows cannot spawn `npm`/`npm.cmd` without a shell (EINVAL since the
5
+ // CVE-2024-27980 hardening), and shell spawns with an args array trip
6
+ // DEP0190. Preferring the npm-cli.js entry point next to the running Node
7
+ // executable avoids both: it is a plain Node script spawn on every platform.
8
+ export function resolveAdjacentNpmCommand(nodeExecutablePath = process.execPath, platform = process.platform) {
9
+ const executableDir = dirname(nodeExecutablePath);
10
+ if (platform === "win32") {
11
+ const npmCliPath = resolve(executableDir, "node_modules", "npm", "bin", "npm-cli.js");
12
+ if (existsSync(npmCliPath)) {
13
+ return { command: nodeExecutablePath, args: [npmCliPath] };
14
+ }
15
+ const npmCmdPath = resolve(executableDir, "npm.cmd");
16
+ return existsSync(npmCmdPath) ? { command: npmCmdPath, args: [], shell: true } : undefined;
17
+ }
18
+
19
+ const candidate = resolve(executableDir, "npm");
20
+ return existsSync(candidate) ? { command: candidate, args: [] } : undefined;
21
+ }
@@ -9,6 +9,7 @@ import { patchAlphaHubAuthSource } from "./lib/alpha-hub-auth-patch.mjs";
9
9
  import { patchAlphaHubSearchResultsSource, patchAlphaHubSearchSource } from "./lib/alpha-hub-search-patch.mjs";
10
10
  import { patchPiAgentCoreSource } from "./lib/pi-agent-core-patch.mjs";
11
11
  import { patchPiExtensionLoaderSource } from "./lib/pi-extension-loader-patch.mjs";
12
+ import { resolveAdjacentNpmCommand } from "./lib/npm-command.mjs";
12
13
  import { patchPiModelRegistrySource } from "./lib/pi-model-registry-patch.mjs";
13
14
  import { patchPiPackageManagerSource } from "./lib/pi-package-manager-patch.mjs";
14
15
  import { patchPiEditorSource, patchPiInteractiveThemeSource, patchPiTuiSource } from "./lib/pi-tui-patch.mjs";
@@ -192,6 +193,18 @@ function resolvePackageManager() {
192
193
  return null;
193
194
  }
194
195
 
196
+ function resolvePackageManagerInvocation(packageManager) {
197
+ if (packageManager === "npm") {
198
+ const adjacent = resolveAdjacentNpmCommand();
199
+ if (adjacent) return adjacent;
200
+ }
201
+ const resolved = resolveExecutable(packageManager);
202
+ if (!resolved) return null;
203
+ // Windows cannot spawn .cmd/.bat shims without a shell (EINVAL).
204
+ const needsShell = process.platform === "win32" && /\.(cmd|bat)$/i.test(resolved);
205
+ return { command: resolved, args: [], shell: needsShell };
206
+ }
207
+
195
208
  function installWorkspacePackages(packageSpecs) {
196
209
  const packageManager = resolvePackageManager();
197
210
  if (!packageManager) {
@@ -201,8 +214,14 @@ function installWorkspacePackages(packageSpecs) {
201
214
  return false;
202
215
  }
203
216
 
204
- const result = spawnSync(packageManager, createInstallCommand(packageManager, packageSpecs), {
217
+ const invocation = resolvePackageManagerInvocation(packageManager);
218
+ if (!invocation) {
219
+ process.stderr.write(`[feynman] could not resolve ${packageManager} executable for bundled package setup.\n`);
220
+ return false;
221
+ }
222
+ const result = spawnSync(invocation.command, [...invocation.args, ...createInstallCommand(packageManager, packageSpecs)], {
205
223
  cwd: workspaceDir,
224
+ shell: invocation.shell,
206
225
  stdio: ["ignore", "pipe", "pipe"],
207
226
  timeout: 300000,
208
227
  env: {
@@ -419,7 +438,11 @@ function restorePackagedWorkspace(packageSpecs) {
419
438
  rmSync(workspaceDir, { recursive: true, force: true });
420
439
  mkdirSync(resolve(appRoot, ".feynman"), { recursive: true });
421
440
 
422
- const result = spawnSync("tar", ["-xzf", workspaceArchivePath, "-C", resolve(appRoot, ".feynman")], {
441
+ // Run tar from .feynman with relative paths: GNU tar (Git for Windows)
442
+ // treats absolute paths with a drive-letter colon ("C:\...") as remote
443
+ // host specs and fails with "Cannot connect ... resolve failed".
444
+ const result = spawnSync("tar", ["-xzf", "runtime-workspace.tgz", "-C", "."], {
445
+ cwd: resolve(appRoot, ".feynman"),
423
446
  stdio: ["ignore", "ignore", "pipe"],
424
447
  timeout: 300000,
425
448
  });
@@ -855,6 +878,23 @@ if (alphaHubIndexPath && existsSync(alphaHubIndexPath)) {
855
878
  }
856
879
  }
857
880
 
881
+ // The bundled workspace carries its own alpha-hub copy; patch it the same way
882
+ // so search fixes apply regardless of which copy resolves at runtime.
883
+ const workspaceAlphaHubLib = resolve(workspaceRoot, "@companion-ai", "alpha-hub", "src", "lib");
884
+ for (const [fileName, patchFn] of [
885
+ ["auth.js", patchAlphaHubAuthSource],
886
+ ["alphaxiv.js", patchAlphaHubSearchSource],
887
+ ["index.js", patchAlphaHubSearchResultsSource],
888
+ ]) {
889
+ const filePath = resolve(workspaceAlphaHubLib, fileName);
890
+ if (!existsSync(filePath)) continue;
891
+ const source = readFileSync(filePath, "utf8");
892
+ const patched = patchFn(source);
893
+ if (patched !== source) {
894
+ writeFileSync(filePath, patched, "utf8");
895
+ }
896
+ }
897
+
858
898
  if (existsSync(piMemoryPath)) {
859
899
  let source = readFileSync(piMemoryPath, "utf8");
860
900
  const memoryOriginal = 'const MEMORY_DIR = join(homedir(), ".pi", "memory");';