@agentprojectcontext/apx 1.22.0 → 1.22.2

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentprojectcontext/apx",
3
- "version": "1.22.0",
3
+ "version": "1.22.2",
4
4
  "description": "APX — unified CLI + daemon for the Agent Project Context (APC) standard.",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -1,5 +1,6 @@
1
1
  import { spawnSync } from "node:child_process";
2
2
  import readline from "node:readline";
3
+ import { fileURLToPath } from "node:url";
3
4
  import { getLatestVersion } from "../../core/update-check.js";
4
5
 
5
6
  const PACKAGE_NAME = "@agentprojectcontext/apx";
@@ -17,11 +18,38 @@ function isNewer(cur, lat) {
17
18
  function hasPnpmGlobal() {
18
19
  const r = spawnSync("pnpm", ["--version"], { encoding: "utf8", stdio: "pipe" });
19
20
  if (r.status !== 0) return false;
20
- // pnpm needs PNPM_HOME configured to manage global packages
21
- const check = spawnSync("pnpm", ["root", "-g"], { encoding: "utf8", stdio: "pipe" });
21
+ // `pnpm add -g` needs a configured global *bin* directory (PNPM_HOME / the
22
+ // result of `pnpm setup`). `pnpm root -g` succeeds even without it, so probe
23
+ // `pnpm bin -g` instead — it fails with ERR_PNPM_NO_GLOBAL_BIN_DIR when the
24
+ // global bin directory is missing.
25
+ const check = spawnSync("pnpm", ["bin", "-g"], { encoding: "utf8", stdio: "pipe" });
22
26
  return check.status === 0 && !!check.stdout?.trim();
23
27
  }
24
28
 
29
+ // Detect which package manager actually owns this apx install, by checking
30
+ // where the running files live. Most installs are npm (it was the recommended
31
+ // installer before the project moved to pnpm), so npm is the safe default
32
+ // when detection is inconclusive.
33
+ function detectInstaller() {
34
+ let selfDir;
35
+ try {
36
+ selfDir = fileURLToPath(import.meta.url);
37
+ } catch {
38
+ selfDir = "";
39
+ }
40
+ const probe = (cmd) => {
41
+ const r = spawnSync(cmd, ["root", "-g"], { encoding: "utf8", stdio: "pipe" });
42
+ return r.status === 0 ? r.stdout?.trim() || "" : "";
43
+ };
44
+ const pnpmRoot = probe("pnpm");
45
+ const npmRoot = probe("npm");
46
+ if (pnpmRoot && selfDir.startsWith(pnpmRoot)) return "pnpm";
47
+ if (npmRoot && selfDir.startsWith(npmRoot)) return "npm";
48
+ // pnpm's global store path contains a "/pnpm/" segment.
49
+ if (/[\\/]pnpm[\\/]/.test(selfDir)) return "pnpm";
50
+ return "npm";
51
+ }
52
+
25
53
  function daemonRunning() {
26
54
  const r = spawnSync("apx", ["daemon", "status", "--json"], { encoding: "utf8", stdio: "pipe" });
27
55
  try { return JSON.parse(r.stdout)?.running === true; } catch { return false; }
@@ -62,15 +90,32 @@ export async function cmdUpdate(args, currentVersion) {
62
90
  console.log("stopped.");
63
91
  }
64
92
 
65
- // Prefer pnpm global if configured, fall back to npm.
66
- const usePnpm = hasPnpmGlobal();
67
- const pm = usePnpm ? "pnpm" : "npm";
68
- const installArgs = usePnpm
69
- ? ["add", "-g", `${PACKAGE_NAME}@${latest}`]
70
- : ["install", "-g", `${PACKAGE_NAME}@${latest}`];
71
-
72
- console.log(`\nInstalling ${PACKAGE_NAME}@${latest} via ${pm}...\n`);
73
- const result = spawnSync(pm, installArgs, { stdio: "inherit" });
93
+ // Install with whichever package manager owns this apx install. npm is the
94
+ // default (most installs predate the move to pnpm). pnpm is used first only
95
+ // when it owns the install AND its global bin directory is configured.
96
+ // The other package manager is always kept as a fallback so a misconfigured
97
+ // pnpm never blocks the update.
98
+ const pnpmStep = ["pnpm", ["add", "-g", `${PACKAGE_NAME}@${latest}`]];
99
+ const npmStep = ["npm", ["install", "-g", `${PACKAGE_NAME}@${latest}`]];
100
+ const pnpmUsable = hasPnpmGlobal();
101
+ const steps =
102
+ detectInstaller() === "pnpm" && pnpmUsable
103
+ ? [pnpmStep, npmStep]
104
+ : pnpmUsable
105
+ ? [npmStep, pnpmStep]
106
+ : [npmStep];
107
+
108
+ let result;
109
+ for (let i = 0; i < steps.length; i++) {
110
+ const [pm, installArgs] = steps[i];
111
+ console.log(`\nInstalling ${PACKAGE_NAME}@${latest} via ${pm}...\n`);
112
+ result = spawnSync(pm, installArgs, { stdio: "inherit" });
113
+ if (result.status === 0) break;
114
+ const next = steps[i + 1];
115
+ if (next) {
116
+ console.log(`\n⚠️ ${pm} install failed — retrying with ${next[0]}...`);
117
+ }
118
+ }
74
119
 
75
120
  if (result.status !== 0) {
76
121
  console.error(`\n❌ Update failed (exit ${result.status})`);