@blueprintit/shop-os-install 0.5.7 → 0.5.8

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.
@@ -20,7 +20,7 @@
20
20
  import { createInterface } from "node:readline/promises";
21
21
  import { stdin, stdout, stderr, exit } from "node:process";
22
22
  import { homedir } from "node:os";
23
- import { join, dirname, resolve } from "node:path";
23
+ import { join, dirname, resolve, delimiter } from "node:path";
24
24
  import { spawnSync } from "node:child_process";
25
25
  import {
26
26
  existsSync,
@@ -35,6 +35,15 @@ const LICENSE_SERVER = "https://shop-os-license-server.glenn-15d.workers.dev";
35
35
  const SUPPORT_URL = "https://blueprintit.ai/shop-os/support";
36
36
  const DOCS_URL = "https://blueprintit.ai/shop-os/docs";
37
37
 
38
+ // Read version from package.json so the user-agent never drifts from the
39
+ // published version. Falls back to "unknown" if the file can't be read.
40
+ let VERSION = "unknown";
41
+ try {
42
+ VERSION = JSON.parse(
43
+ readFileSync(new URL("../package.json", import.meta.url), "utf8"),
44
+ ).version;
45
+ } catch { /* keep "unknown" */ }
46
+
38
47
  const MARKETPLACES = [
39
48
  {
40
49
  name: "blueprint-skills",
@@ -124,21 +133,59 @@ function checkClaudeCode() {
124
133
  // setup scripts now do), the .claude directory isn't created until the user
125
134
  // launches `claude` for the first time. A binary check correctly identifies
126
135
  // installs from npm, the official .ps1/.sh installer, or the desktop app.
136
+ //
137
+ // The native installer drops `claude` in ~/.local/bin, which a fresh shell may
138
+ // not have on PATH yet. Prepend it before probing so a real install is never
139
+ // mistaken for missing (the exact failure that stalled early installs).
140
+ const localBin = join(homedir(), ".local", "bin");
141
+ if (existsSync(localBin) && !(process.env.PATH || "").split(delimiter).includes(localBin)) {
142
+ process.env.PATH = localBin + delimiter + (process.env.PATH || "");
143
+ }
127
144
  const probe = spawnSync(
128
145
  process.platform === "win32" ? "where" : "which",
129
146
  ["claude"],
130
147
  { stdio: "ignore", shell: false },
131
148
  );
132
- if (probe.status !== 0) {
133
- print("");
134
- print(red("Claude Code is not installed."));
135
- print("");
136
- print("Shop OS runs on top of Claude Code. Install it first at:");
137
- print(" " + cyan("https://claude.ai/code"));
149
+ if (probe.status === 0) {
150
+ ok("Claude Code found");
151
+ } else {
152
+ // Claude Code not in PATH. Try auto-installing via npm.
153
+ // shell: true is REQUIRED on Windows npm is npm.cmd (a batch file), and
154
+ // spawnSync can't execute it without a shell (ENOENT otherwise). stdio
155
+ // inherit so the customer sees npm's progress instead of a frozen prompt.
138
156
  print("");
139
- print("Once Claude Code is installed and you have signed in once,");
140
- print("re-run this installer.");
141
- exit(1);
157
+ print(yellow("Claude Code not found. Auto-installing via npm..."));
158
+ const npmInstall = spawnSync("npm", ["install", "-g", "@anthropic-ai/claude-code"], {
159
+ stdio: "inherit",
160
+ shell: true,
161
+ });
162
+ if (npmInstall.status !== 0) {
163
+ print("");
164
+ print(red("Claude Code auto-install failed."));
165
+ print("");
166
+ print("Shop OS runs on top of Claude Code. Install it manually at:");
167
+ print(" " + cyan("https://claude.ai/code"));
168
+ print("");
169
+ print("Then re-run this installer.");
170
+ exit(1);
171
+ }
172
+ // Verify the install by checking PATH again (may need a refresh on Windows).
173
+ const verify = spawnSync(
174
+ process.platform === "win32" ? "where" : "which",
175
+ ["claude"],
176
+ { stdio: "ignore", shell: false },
177
+ );
178
+ if (verify.status !== 0) {
179
+ print("");
180
+ print(red("Claude Code installed but `claude` is not on PATH."));
181
+ print("");
182
+ print("This may be a PATH refresh issue. Please:");
183
+ print(" 1. Close this terminal");
184
+ print(" 2. Open a new terminal");
185
+ print(" 3. Re-run the installer");
186
+ exit(1);
187
+ }
188
+ ok("Claude Code installed and verified");
142
189
  }
143
190
  // Stage ~/.claude so downstream marketplace and plugin writes succeed even
144
191
  // if the user hasn't launched `claude` yet to seed the dir themselves.
@@ -155,7 +202,7 @@ async function validateLicense(key) {
155
202
  const url = `${LICENSE_SERVER}/validate?key=${encodeURIComponent(key)}`;
156
203
  let resp;
157
204
  try {
158
- resp = await fetch(url, { headers: { "user-agent": "shop-os-installer/0.5.3" } });
205
+ resp = await fetch(url, { headers: { "user-agent": `shop-os-installer/${VERSION}` } });
159
206
  } catch (e) {
160
207
  return { ok: false, error: `network: ${e.message}` };
161
208
  }
@@ -13,7 +13,7 @@
13
13
  */
14
14
 
15
15
  import { homedir } from "node:os";
16
- import { join, dirname } from "node:path";
16
+ import { join, dirname, delimiter } from "node:path";
17
17
  import { spawnSync } from "node:child_process";
18
18
  import {
19
19
  existsSync,
@@ -69,10 +69,22 @@ function banner() {
69
69
  ].forEach((l) => print(l));
70
70
  }
71
71
 
72
+ function ensureLocalBinOnPath() {
73
+ // The Claude native installer drops `claude` in ~/.local/bin (all platforms).
74
+ // A fresh shell may not have that on PATH yet, which would make a real install
75
+ // look missing. Prepend it before probing so detection never false-fails.
76
+ const localBin = join(homedir(), ".local", "bin");
77
+ const current = process.env.PATH || "";
78
+ if (existsSync(localBin) && !current.split(delimiter).includes(localBin)) {
79
+ process.env.PATH = localBin + delimiter + current;
80
+ }
81
+ }
82
+
72
83
  function preflight() {
73
84
  const major = Number(process.versions.node.split(".")[0]);
74
85
  if (major < 18) fail(`Node.js 18+ required. You have ${process.version}.`);
75
86
 
87
+ ensureLocalBinOnPath();
76
88
  const probe = spawnSync(
77
89
  process.platform === "win32" ? "where" : "which",
78
90
  ["claude"],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blueprintit/shop-os-install",
3
- "version": "0.5.7",
3
+ "version": "0.5.8",
4
4
  "description": "One-command installer for Shop OS — Blueprint IT's AI Operating System for small businesses.",
5
5
  "type": "module",
6
6
  "bin": {