@companion-ai/feynman 0.2.1 → 0.2.3

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,5 +1,5 @@
1
1
  import { readdir } from "node:fs/promises";
2
- import { cpus, freemem, homedir, totalmem } from "node:os";
2
+ import { cpus, homedir, totalmem } from "node:os";
3
3
  import { execSync } from "node:child_process";
4
4
  import { resolve as resolvePath } from "node:path";
5
5
 
@@ -133,33 +133,30 @@ type SystemResources = {
133
133
  docker: boolean;
134
134
  };
135
135
 
136
+ let cachedResources: SystemResources | null = null;
137
+
136
138
  function detectSystemResources(): SystemResources {
139
+ if (cachedResources) return cachedResources;
140
+
137
141
  const cores = cpus().length;
138
- const cpu = cpus()[0]?.model?.trim() ?? "unknown";
139
142
  const totalBytes = totalmem();
140
- const freeBytes = freemem();
141
143
  const ramTotal = `${Math.round(totalBytes / (1024 ** 3))}GB`;
142
- const ramFree = `${Math.round(freeBytes / (1024 ** 3))}GB`;
143
144
 
144
- let gpu: string | null = null;
145
+ cachedResources = { cpu: "", cores, ramTotal, ramFree: "", gpu: null, docker: false };
146
+
145
147
  try {
146
148
  if (process.platform === "darwin") {
147
- const out = execSync("system_profiler SPDisplaysDataType 2>/dev/null | grep 'Chipset Model\\|Chip Model'", { encoding: "utf8", timeout: 3000 }).trim();
148
- const match = out.match(/:\s*(.+)/);
149
- if (match) gpu = match[1]!.trim();
150
- } else {
151
- const out = execSync("nvidia-smi --query-gpu=name --format=csv,noheader 2>/dev/null", { encoding: "utf8", timeout: 3000 }).trim();
152
- if (out) gpu = out.split("\n")[0]!.trim();
149
+ const out = execSync("sysctl -n machdep.cpu.brand_string 2>/dev/null", { encoding: "utf8", timeout: 1000 }).trim();
150
+ if (out) cachedResources.cpu = out;
153
151
  }
154
152
  } catch {}
155
153
 
156
- let docker = false;
157
154
  try {
158
- execSync("docker info 2>/dev/null", { timeout: 3000 });
159
- docker = true;
155
+ execSync("command -v docker >/dev/null 2>&1", { timeout: 500 });
156
+ cachedResources.docker = true;
160
157
  } catch {}
161
158
 
162
- return { cpu, cores, ramTotal, ramFree, gpu, docker };
159
+ return cachedResources;
163
160
  }
164
161
 
165
162
  type WorkflowInfo = { name: string; description: string };
@@ -268,10 +265,9 @@ export function installFeynmanHeader(
268
265
  pushLabeled("directory", dirLabel, "text");
269
266
  pushLabeled("session", sessionId, "dim");
270
267
  leftLines.push("");
271
- pushLabeled("cpu", `${resources.cores} cores`, "dim");
272
- pushLabeled("ram", `${resources.ramFree} free / ${resources.ramTotal}`, "dim");
273
- if (resources.gpu) pushLabeled("gpu", resources.gpu, "dim");
274
- pushLabeled("docker", resources.docker ? "available" : "not found", "dim");
268
+ const sysParts = [`${resources.cores} cores`, resources.ramTotal];
269
+ if (resources.docker) sysParts.push("docker");
270
+ pushLabeled("system", sysParts.join(" · "), "dim");
275
271
  leftLines.push("");
276
272
  leftLines.push(theme.fg("dim", `${toolCount} tools · ${agentCount} agents`));
277
273
 
@@ -343,7 +339,7 @@ export function installFeynmanHeader(
343
339
  push(row(`${theme.fg("dim", "model".padEnd(10))} ${theme.fg("text", truncateVisible(modelLabel, narrowValW))}`));
344
340
  push(row(`${theme.fg("dim", "directory".padEnd(10))} ${theme.fg("text", truncateVisible(dirLabel, narrowValW))}`));
345
341
  push(row(`${theme.fg("dim", "session".padEnd(10))} ${theme.fg("dim", truncateVisible(sessionId, narrowValW))}`));
346
- const resourceLine = `${resources.cores} cores · ${resources.ramTotal} ram${resources.gpu ? ` · ${resources.gpu}` : ""}${resources.docker ? " · docker" : ""}`;
342
+ const resourceLine = `${resources.cores} cores · ${resources.ramTotal}${resources.docker ? " · docker" : ""}`;
347
343
  push(row(theme.fg("dim", truncateVisible(resourceLine, contentW))));
348
344
  push(row(theme.fg("dim", truncateVisible(`${toolCount} tools · ${agentCount} agents · ${commandCount} commands`, contentW))));
349
345
  push(emptyRow());
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@companion-ai/feynman",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
4
4
  "description": "Research-first CLI agent built on Pi and alphaXiv",
5
5
  "type": "module",
6
6
  "bin": {
@@ -27,53 +27,7 @@ const settingsPath = resolve(appRoot, ".feynman", "settings.json");
27
27
  const workspaceDir = resolve(appRoot, ".feynman", "npm");
28
28
  const workspacePackageJsonPath = resolve(workspaceDir, "package.json");
29
29
 
30
- function ensurePackageWorkspace() {
31
- if (!existsSync(settingsPath)) {
32
- return;
33
- }
34
-
35
- const settings = JSON.parse(readFileSync(settingsPath, "utf8"));
36
- const packageSpecs = Array.isArray(settings.packages)
37
- ? settings.packages
38
- .filter((value) => typeof value === "string" && value.startsWith("npm:"))
39
- .map((value) => value.slice(4))
40
- : [];
41
-
42
- if (packageSpecs.length === 0) {
43
- return;
44
- }
45
-
46
- mkdirSync(workspaceDir, { recursive: true });
47
-
48
- writeFileSync(
49
- workspacePackageJsonPath,
50
- JSON.stringify(
51
- {
52
- name: "pi-extensions",
53
- private: true,
54
- dependencies: Object.fromEntries(packageSpecs.map((spec) => [spec, "latest"])),
55
- },
56
- null,
57
- 2,
58
- ) + "\n",
59
- "utf8",
60
- );
61
-
62
- const npmExec = process.env.npm_execpath;
63
- const install = npmExec
64
- ? spawnSync(process.execPath, [npmExec, "install", "--prefix", workspaceDir, ...packageSpecs], {
65
- stdio: "inherit",
66
- })
67
- : spawnSync("npm", ["install", "--prefix", workspaceDir, ...packageSpecs], {
68
- stdio: "inherit",
69
- });
70
-
71
- if (install.status !== 0) {
72
- console.warn("[feynman] warning: failed to preinstall default Pi packages into .feynman/npm");
73
- }
74
- }
75
-
76
- ensurePackageWorkspace();
30
+ // Pi handles package installation from .feynman/settings.json at runtime — no manual install needed
77
31
 
78
32
  if (existsSync(packageJsonPath)) {
79
33
  const pkg = JSON.parse(readFileSync(packageJsonPath, "utf8"));
@@ -296,6 +250,18 @@ if (existsSync(sessionSearchIndexerPath)) {
296
250
  }
297
251
  }
298
252
 
253
+ const oauthPagePath = resolve(appRoot, "node_modules", "@mariozechner", "pi-ai", "dist", "utils", "oauth", "oauth-page.js");
254
+
255
+ if (existsSync(oauthPagePath)) {
256
+ let source = readFileSync(oauthPagePath, "utf8");
257
+ const piLogo = 'const LOGO_SVG = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 800" aria-hidden="true"><path fill="#fff" fill-rule="evenodd" d="M165.29 165.29 H517.36 V400 H400 V517.36 H282.65 V634.72 H165.29 Z M282.65 282.65 V400 H400 V282.65 Z"/><path fill="#fff" d="M517.36 400 H634.72 V634.72 H517.36 Z"/></svg>`;';
258
+ if (source.includes(piLogo)) {
259
+ const feynmanLogo = 'const LOGO_SVG = `<span style="font-size:32px;font-weight:700;color:#10b981;font-family:system-ui,sans-serif;letter-spacing:-0.02em">feynman</span>`;';
260
+ source = source.replace(piLogo, feynmanLogo);
261
+ writeFileSync(oauthPagePath, source, "utf8");
262
+ }
263
+ }
264
+
299
265
  if (existsSync(piMemoryPath)) {
300
266
  let source = readFileSync(piMemoryPath, "utf8");
301
267
  const memoryOriginal = 'const MEMORY_DIR = join(homedir(), ".pi", "memory");';