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.
- package/README.md +2 -2
- package/install.js +31 -59
- 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
|
|
18
|
-
|
|
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 {
|
|
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
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
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
|
|
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
|
|
74
|
+
async function pipInstallWithRetry(pip, spec) {
|
|
80
75
|
for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
|
|
81
76
|
try {
|
|
82
|
-
log(
|
|
83
|
-
|
|
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(`
|
|
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
|
|
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.
|
|
133
|
-
║
|
|
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(
|
|
148
|
-
|
|
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
|
-
//
|
|
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
|
-
|
|
174
|
-
stdio: "pipe",
|
|
175
|
-
});
|
|
147
|
+
await pipInstallWithRetry(pip, PYPI_SPEC);
|
|
176
148
|
} catch (err) {
|
|
177
|
-
warn(
|
|
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.
|
|
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
|
-
|
|
41
|
-
"postinstall": "node install.js"
|
|
42
|
-
}
|
|
43
|
-
}
|
|
42
|
+
]
|
|
43
|
+
}
|