buildwithtrace 0.1.1 → 0.1.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.
Files changed (3) hide show
  1. package/README.md +2 -2
  2. package/install.js +31 -59
  3. package/package.json +6 -6
package/README.md CHANGED
@@ -14,8 +14,8 @@ npm install -g buildwithtrace
14
14
  # Or via pip
15
15
  pip install buildwithtrace
16
16
 
17
- # Or via standalone installer
18
- curl -fsSL https://buildwithtrace.com/install.sh | sh
17
+ # Or via Homebrew
18
+ brew install buildwithtrace/tap/buildwithtrace
19
19
  ```
20
20
 
21
21
  ## Requirements
package/install.js CHANGED
@@ -1,17 +1,19 @@
1
1
  #!/usr/bin/env node
2
2
  "use strict";
3
3
 
4
- const { execSync, execFileSync } = require("child_process");
4
+ const { execFileSync } = require("child_process");
5
5
  const fs = require("fs");
6
6
  const path = require("path");
7
- const { pipeline } = require("stream/promises");
8
7
 
9
- // Derive the wheel name from package.json so a version bump (release.yml runs
10
- // `npm version` before publishing) downloads the matching release asset instead of a
11
- // stale, hardcoded one. `latest/download/<file>` resolves to the newest release's asset.
8
+ // Install the matching CLI version straight from PyPI (PUBLIC). The wheel + its deps
9
+ // (buildwithtrace-sdk, buildwithtrace-converter) all resolve from PyPI. We deliberately
10
+ // do NOT fetch a GitHub release asset: the CLI repo (buildwithtrace/cli) is PRIVATE, so
11
+ // its release tarball/wheel is NOT publicly downloadable — an unauthenticated fetch 404s.
12
+ // (This is the same reason the Homebrew formula points at the PyPI sdist.) The npm
13
+ // wrapper version is kept in lockstep with the PyPI release by publish.sh, so pinning the
14
+ // exact version is safe — the matching PyPI release always exists before users run npx.
12
15
  const PKG_VERSION = require("./package.json").version;
13
- const WHEEL_NAME = `buildwithtrace-${PKG_VERSION}-py3-none-any.whl`;
14
- const WHEEL_URL = `https://github.com/buildwithtrace/cli/releases/latest/download/${WHEEL_NAME}`;
16
+ const PYPI_SPEC = `buildwithtrace==${PKG_VERSION}`;
15
17
  const VENV_DIR = path.join(
16
18
  __dirname,
17
19
  "node_modules",
@@ -62,13 +64,6 @@ function findPython() {
62
64
  return null;
63
65
  }
64
66
 
65
- function getPythonBin() {
66
- if (process.platform === "win32") {
67
- return path.join(VENV_DIR, "Scripts", "python.exe");
68
- }
69
- return path.join(VENV_DIR, "bin", "python3");
70
- }
71
-
72
67
  function getPipBin() {
73
68
  if (process.platform === "win32") {
74
69
  return path.join(VENV_DIR, "Scripts", "pip.exe");
@@ -76,36 +71,27 @@ function getPipBin() {
76
71
  return path.join(VENV_DIR, "bin", "pip");
77
72
  }
78
73
 
79
- async function downloadWithRetry(url, destPath) {
74
+ async function pipInstallWithRetry(pip, spec) {
80
75
  for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
81
76
  try {
82
- log(
83
- `Downloading wheel (attempt ${attempt}/${MAX_RETRIES})...`
77
+ log(`Installing ${spec} from PyPI (attempt ${attempt}/${MAX_RETRIES})...`);
78
+ execFileSync(
79
+ pip,
80
+ ["install", "--quiet", "--no-cache-dir", "--upgrade", spec],
81
+ { stdio: "pipe" }
84
82
  );
85
-
86
- const response = await fetch(url, { redirect: "follow" });
87
- if (!response.ok) {
88
- throw new Error(`HTTP ${response.status}: ${response.statusText}`);
89
- }
90
-
91
- const fileStream = fs.createWriteStream(destPath);
92
- await pipeline(response.body, fileStream);
93
-
94
- const stats = fs.statSync(destPath);
95
- if (stats.size === 0) {
96
- throw new Error("Downloaded file is empty");
97
- }
98
-
99
- log(`Downloaded ${(stats.size / 1024 / 1024).toFixed(1)} MB`);
83
+ log("Installed from PyPI.");
100
84
  return;
101
85
  } catch (err) {
86
+ const stderr = err.stderr && err.stderr.toString().trim();
87
+ const detail = (stderr && stderr.split("\n").pop()) || err.message;
102
88
  if (attempt < MAX_RETRIES) {
103
89
  const delay = attempt * 2000;
104
- warn(`Download failed: ${err.message}. Retrying in ${delay / 1000}s...`);
90
+ warn(`Install failed: ${detail}. Retrying in ${delay / 1000}s...`);
105
91
  await new Promise((r) => setTimeout(r, delay));
106
92
  } else {
107
93
  throw new Error(
108
- `Failed to download after ${MAX_RETRIES} attempts: ${err.message}`
94
+ `Failed to install after ${MAX_RETRIES} attempts: ${detail}`
109
95
  );
110
96
  }
111
97
  }
@@ -119,7 +105,7 @@ async function main() {
119
105
  if (!python) {
120
106
  console.error(`
121
107
  ╔══════════════════════════════════════════════════════════════════╗
122
- ║ buildwithtrace requires Python 3.10 or later
108
+ ║ buildwithtrace requires Python 3.10 or later
123
109
  ╠══════════════════════════════════════════════════════════════════╣
124
110
  ║ ║
125
111
  ║ Python was not found on your system. Install options: ║
@@ -129,8 +115,8 @@ async function main() {
129
115
  ║ 2. Install directly via pip (if Python is available elsewhere): ║
130
116
  ║ pip install buildwithtrace ║
131
117
  ║ ║
132
- ║ 3. Use the standalone installer:
133
- curl -fsSL https://buildwithtrace.com/install.sh | sh
118
+ ║ 3. Or via Homebrew (installs Python for you):
119
+ brew install buildwithtrace/tap/buildwithtrace
134
120
  ║ ║
135
121
  ║ After installing Python, run: npx buildwithtrace ║
136
122
  ╚══════════════════════════════════════════════════════════════════╝
@@ -144,37 +130,23 @@ async function main() {
144
130
 
145
131
  const pythonParts = python.split(" ");
146
132
  try {
147
- execFileSync(pythonParts[0], [...pythonParts.slice(1), "-m", "venv", VENV_DIR], {
148
- stdio: "pipe",
149
- });
133
+ execFileSync(
134
+ pythonParts[0],
135
+ [...pythonParts.slice(1), "-m", "venv", VENV_DIR],
136
+ { stdio: "pipe" }
137
+ );
150
138
  } catch (err) {
151
139
  warn(`Failed to create venv: ${err.message}`);
152
140
  warn("The CLI will fall back to system-installed buildwithtrace.");
153
141
  process.exit(0);
154
142
  }
155
143
 
156
- // Download wheel
157
- const wheelDir = path.join(VENV_DIR, ".wheels");
158
- fs.mkdirSync(wheelDir, { recursive: true });
159
- const wheelPath = path.join(wheelDir, WHEEL_NAME);
160
-
161
- try {
162
- await downloadWithRetry(WHEEL_URL, wheelPath);
163
- } catch (err) {
164
- warn(`${err.message}`);
165
- warn("The CLI will fall back to system-installed buildwithtrace.");
166
- process.exit(0);
167
- }
168
-
169
- // Install wheel into venv
170
- log("Installing buildwithtrace into virtual environment...");
144
+ // Install the CLI (+ its deps) from PyPI into the venv
171
145
  const pip = getPipBin();
172
146
  try {
173
- execFileSync(pip, ["install", "--quiet", "--no-cache-dir", wheelPath], {
174
- stdio: "pipe",
175
- });
147
+ await pipInstallWithRetry(pip, PYPI_SPEC);
176
148
  } catch (err) {
177
- warn(`pip install failed: ${err.message}`);
149
+ warn(`${err.message}`);
178
150
  warn("The CLI will fall back to system-installed buildwithtrace.");
179
151
  process.exit(0);
180
152
  }
package/package.json CHANGED
@@ -1,10 +1,13 @@
1
1
  {
2
2
  "name": "buildwithtrace",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "AI-powered PCB design CLI. Design, review, convert, and manufacture circuit boards from your terminal.",
5
5
  "bin": {
6
6
  "buildwithtrace": "./bin/buildwithtrace.js"
7
7
  },
8
+ "scripts": {
9
+ "postinstall": "node install.js"
10
+ },
8
11
  "keywords": [
9
12
  "pcb",
10
13
  "eda",
@@ -36,8 +39,5 @@
36
39
  "install.js",
37
40
  "README.md",
38
41
  "LICENSE"
39
- ],
40
- "scripts": {
41
- "postinstall": "node install.js"
42
- }
43
- }
42
+ ]
43
+ }