@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.
- package/bin/shop-os-install.js +58 -11
- package/bin/shop-os-update.js +13 -1
- package/package.json +1 -1
package/bin/shop-os-install.js
CHANGED
|
@@ -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
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
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("
|
|
140
|
-
|
|
141
|
-
|
|
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":
|
|
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
|
}
|
package/bin/shop-os-update.js
CHANGED
|
@@ -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"],
|