@toothfairyai/tfcode 2.7.0 → 2.7.1
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 +13 -13
- package/postinstall.mjs +46 -188
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@toothfairyai/tfcode",
|
|
3
|
-
"version": "2.7.
|
|
3
|
+
"version": "2.7.1",
|
|
4
4
|
"bin": {
|
|
5
5
|
"tfcode": "./bin/tfcode.js"
|
|
6
6
|
},
|
|
@@ -9,18 +9,18 @@
|
|
|
9
9
|
},
|
|
10
10
|
"license": "MIT",
|
|
11
11
|
"optionalDependencies": {
|
|
12
|
-
"@toothfairyai/tfcode-linux-arm64": "2.7.
|
|
13
|
-
"@toothfairyai/tfcode-windows-x64": "2.7.
|
|
14
|
-
"@toothfairyai/tfcode-linux-x64-baseline-musl": "2.7.
|
|
15
|
-
"@toothfairyai/tfcode-darwin-x64-baseline": "2.7.
|
|
16
|
-
"@toothfairyai/tfcode-linux-x64-musl": "2.7.
|
|
17
|
-
"@toothfairyai/tfcode-windows-x64-baseline": "2.7.
|
|
18
|
-
"@toothfairyai/tfcode-linux-arm64-musl": "2.7.
|
|
19
|
-
"@toothfairyai/tfcode-windows-arm64": "2.7.
|
|
20
|
-
"@toothfairyai/tfcode-linux-x64": "2.7.
|
|
21
|
-
"@toothfairyai/tfcode-darwin-x64": "2.7.
|
|
22
|
-
"@toothfairyai/tfcode-linux-x64-baseline": "2.7.
|
|
23
|
-
"@toothfairyai/tfcode-darwin-arm64": "2.7.
|
|
12
|
+
"@toothfairyai/tfcode-linux-arm64": "2.7.1",
|
|
13
|
+
"@toothfairyai/tfcode-windows-x64": "2.7.1",
|
|
14
|
+
"@toothfairyai/tfcode-linux-x64-baseline-musl": "2.7.1",
|
|
15
|
+
"@toothfairyai/tfcode-darwin-x64-baseline": "2.7.1",
|
|
16
|
+
"@toothfairyai/tfcode-linux-x64-musl": "2.7.1",
|
|
17
|
+
"@toothfairyai/tfcode-windows-x64-baseline": "2.7.1",
|
|
18
|
+
"@toothfairyai/tfcode-linux-arm64-musl": "2.7.1",
|
|
19
|
+
"@toothfairyai/tfcode-windows-arm64": "2.7.1",
|
|
20
|
+
"@toothfairyai/tfcode-linux-x64": "2.7.1",
|
|
21
|
+
"@toothfairyai/tfcode-darwin-x64": "2.7.1",
|
|
22
|
+
"@toothfairyai/tfcode-linux-x64-baseline": "2.7.1",
|
|
23
|
+
"@toothfairyai/tfcode-darwin-arm64": "2.7.1"
|
|
24
24
|
},
|
|
25
25
|
"engines": {
|
|
26
26
|
"node": ">=18"
|
package/postinstall.mjs
CHANGED
|
@@ -9,9 +9,6 @@ import { fileURLToPath } from "url"
|
|
|
9
9
|
const __filename = fileURLToPath(import.meta.url)
|
|
10
10
|
const __dirname = path.dirname(__filename)
|
|
11
11
|
|
|
12
|
-
const GITEA_HOST = process.env.TFCODE_GITEA_HOST || "gitea.toothfairyai.com"
|
|
13
|
-
const GITEA_REPO = process.env.TFCODE_GITEA_REPO || "ToothFairyAI/tf_code"
|
|
14
|
-
|
|
15
12
|
function detectPlatform() {
|
|
16
13
|
let platform
|
|
17
14
|
switch (os.platform()) {
|
|
@@ -72,180 +69,12 @@ function detectPlatform() {
|
|
|
72
69
|
return { platform, arch, needsBaseline, abi }
|
|
73
70
|
}
|
|
74
71
|
|
|
75
|
-
|
|
76
|
-
try {
|
|
77
|
-
const res = await fetch(`https://${GITEA_HOST}/api/v1/repos/${GITEA_REPO}/releases/latest`)
|
|
78
|
-
const data = await res.json()
|
|
79
|
-
return data.tag_name?.replace(/^v/, "") || "1.0.0"
|
|
80
|
-
} catch {
|
|
81
|
-
return "1.0.0"
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// Detect the machine architecture of an existing binary. Returns "arm64",
|
|
86
|
-
// "x64", or null when it can't be determined (null => keep existing binary).
|
|
87
|
-
function detectBinaryMachineArch(binaryPath, platform) {
|
|
88
|
-
try {
|
|
89
|
-
if (platform === "win32") {
|
|
90
|
-
const buf = fs.readFileSync(binaryPath)
|
|
91
|
-
if (buf.length < 0x40) return null
|
|
92
|
-
const pe = buf.readUInt32LE(0x3c)
|
|
93
|
-
if (pe + 6 > buf.length) return null
|
|
94
|
-
const machine = buf.readUInt16LE(pe + 4)
|
|
95
|
-
if (machine === 0x8664) return "x64"
|
|
96
|
-
if (machine === 0xaa64) return "arm64"
|
|
97
|
-
return null
|
|
98
|
-
}
|
|
99
|
-
const out = spawnSync("file", [binaryPath], { encoding: "utf8" }).stdout || ""
|
|
100
|
-
if (/arm64|aarch64/i.test(out)) return "arm64"
|
|
101
|
-
if (/x86-64|x86_64/i.test(out)) return "x64"
|
|
102
|
-
return null
|
|
103
|
-
} catch {
|
|
104
|
-
return null
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// A missing/unparseable binary is treated as a match so installs never break
|
|
109
|
-
// when detection isn't possible; a real mismatch forces a re-download.
|
|
110
|
-
function binaryArchMatches(binaryPath, expectedArch, platform) {
|
|
111
|
-
const actual = detectBinaryMachineArch(binaryPath, platform)
|
|
112
|
-
if (!actual) return true
|
|
113
|
-
return actual === expectedArch
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
async function downloadBinary() {
|
|
72
|
+
function targetName() {
|
|
117
73
|
const { platform, arch, needsBaseline, abi } = detectPlatform()
|
|
118
|
-
const version = await getVersion()
|
|
119
|
-
|
|
120
|
-
// Check if binary already exists (included in npm package)
|
|
121
|
-
const binaryName = platform === "windows" ? "tfcode.exe" : "tfcode"
|
|
122
|
-
const binDir = path.join(__dirname, "bin")
|
|
123
|
-
const existingBinary = path.join(binDir, binaryName)
|
|
124
|
-
|
|
125
|
-
if (fs.existsSync(existingBinary) && binaryArchMatches(existingBinary, arch, platform)) {
|
|
126
|
-
console.log(`✓ Binary already exists at ${existingBinary}`)
|
|
127
|
-
// Still need to copy app dir from platform package if missing
|
|
128
|
-
const targetAppDir = path.join(binDir, "app")
|
|
129
|
-
if (!fs.existsSync(path.join(targetAppDir, "dist", "index.html"))) {
|
|
130
|
-
let target = `tfcode-${platform}-${arch}`
|
|
131
|
-
if (needsBaseline) target += "-baseline"
|
|
132
|
-
if (abi) target += `-${abi}`
|
|
133
|
-
const pkgName = `@toothfairyai/${target}`
|
|
134
|
-
try {
|
|
135
|
-
const pkgUrl = import.meta.resolve(`${pkgName}/package.json`)
|
|
136
|
-
const pkgDir = path.dirname(fileURLToPath(pkgUrl))
|
|
137
|
-
const srcAppDir = path.join(pkgDir, "bin", "app")
|
|
138
|
-
if (fs.existsSync(path.join(srcAppDir, "dist", "index.html"))) {
|
|
139
|
-
fs.cpSync(srcAppDir, targetAppDir, { recursive: true })
|
|
140
|
-
console.log("✓ Web app copied from platform package")
|
|
141
|
-
}
|
|
142
|
-
copyComputerAssets(path.join(pkgDir, "bin"), binDir)
|
|
143
|
-
} catch (e) {
|
|
144
|
-
console.log(`! Could not copy web app: ${e.message}`)
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
return
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
// Build filename for download
|
|
151
74
|
let target = `tfcode-${platform}-${arch}`
|
|
152
75
|
if (needsBaseline) target += "-baseline"
|
|
153
76
|
if (abi) target += `-${abi}`
|
|
154
|
-
|
|
155
|
-
const ext = platform === "linux" ? ".tar.gz" : ".zip"
|
|
156
|
-
const filename = `${target}${ext}`
|
|
157
|
-
const url = `https://${GITEA_HOST}/${GITEA_REPO}/releases/download/v${version}/${filename}`
|
|
158
|
-
|
|
159
|
-
console.log(`Downloading tfcode v${version} for ${target}...`)
|
|
160
|
-
|
|
161
|
-
// Download
|
|
162
|
-
if (!fs.existsSync(binDir)) fs.mkdirSync(binDir, { recursive: true })
|
|
163
|
-
|
|
164
|
-
const tmpDir = path.join(os.tmpdir(), `tfcode-install-${process.pid}`)
|
|
165
|
-
fs.mkdirSync(tmpDir, { recursive: true })
|
|
166
|
-
const archivePath = path.join(tmpDir, filename)
|
|
167
|
-
|
|
168
|
-
// Use curl to download
|
|
169
|
-
const curlResult = spawnSync("curl", ["-fsSL", "-o", archivePath, url], { stdio: "inherit" })
|
|
170
|
-
if (curlResult.status !== 0) {
|
|
171
|
-
console.error(`Failed to download from ${url}`)
|
|
172
|
-
console.error("Trying npm package fallback...")
|
|
173
|
-
|
|
174
|
-
// Fallback to npm optionalDependencies
|
|
175
|
-
try {
|
|
176
|
-
const pkgName = `@toothfairyai/${target}`
|
|
177
|
-
|
|
178
|
-
// ESM-compatible module resolution
|
|
179
|
-
let pkgDir
|
|
180
|
-
try {
|
|
181
|
-
const pkgUrl = import.meta.resolve(`${pkgName}/package.json`)
|
|
182
|
-
const pkgPath = fileURLToPath(pkgUrl)
|
|
183
|
-
pkgDir = path.dirname(pkgPath)
|
|
184
|
-
} catch (e) {
|
|
185
|
-
console.error(`Could not resolve ${pkgName}:`, e.message)
|
|
186
|
-
throw e
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
const binaryName = platform === "windows" ? "tfcode.exe" : "tfcode"
|
|
190
|
-
const binaryPath = path.join(pkgDir, "bin", binaryName)
|
|
191
|
-
|
|
192
|
-
console.log(`Looking for binary at: ${binaryPath}`)
|
|
193
|
-
|
|
194
|
-
if (fs.existsSync(binaryPath)) {
|
|
195
|
-
console.log(`Found binary at npm package location`)
|
|
196
|
-
setupBinary(binaryPath, platform)
|
|
197
|
-
return
|
|
198
|
-
} else {
|
|
199
|
-
console.error(`Binary not found at ${binaryPath}`)
|
|
200
|
-
}
|
|
201
|
-
} catch (e) {
|
|
202
|
-
console.error("npm package fallback failed:", e.message)
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
console.error("")
|
|
206
|
-
console.error("Installation failed. The binary could not be downloaded or found.")
|
|
207
|
-
console.error("")
|
|
208
|
-
console.error("Possible solutions:")
|
|
209
|
-
console.error(" 1. If this is a private installation, set TFCODE_GITEA_HOST to an accessible host")
|
|
210
|
-
console.error(" 2. Manually download the binary and place it in the bin/ directory")
|
|
211
|
-
console.error(" 3. Contact ToothFairyAI support for assistance")
|
|
212
|
-
console.error("")
|
|
213
|
-
process.exit(1)
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
// Extract
|
|
217
|
-
console.log("Extracting...")
|
|
218
|
-
if (platform === "linux") {
|
|
219
|
-
spawnSync("tar", ["-xzf", archivePath, "-C", tmpDir], { stdio: "inherit" })
|
|
220
|
-
} else {
|
|
221
|
-
spawnSync("unzip", ["-q", archivePath, "-d", tmpDir], { stdio: "inherit" })
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
// Move binary
|
|
225
|
-
const extractedBinary = path.join(tmpDir, "tfcode")
|
|
226
|
-
const targetBinary = path.join(binDir, binaryName)
|
|
227
|
-
|
|
228
|
-
if (fs.existsSync(extractedBinary)) {
|
|
229
|
-
if (fs.existsSync(targetBinary)) fs.unlinkSync(targetBinary)
|
|
230
|
-
fs.copyFileSync(extractedBinary, targetBinary)
|
|
231
|
-
fs.chmodSync(targetBinary, 0o755)
|
|
232
|
-
console.log(`Installed tfcode to ${targetBinary}`)
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
// Move app directory
|
|
236
|
-
const extractedAppDir = path.join(tmpDir, "app")
|
|
237
|
-
const targetAppDir = path.join(binDir, "app")
|
|
238
|
-
if (fs.existsSync(path.join(extractedAppDir, "dist", "index.html"))) {
|
|
239
|
-
if (fs.existsSync(targetAppDir)) fs.rmSync(targetAppDir, { recursive: true, force: true })
|
|
240
|
-
fs.cpSync(extractedAppDir, targetAppDir, { recursive: true })
|
|
241
|
-
console.log("Installed web app")
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
// Move bundled computer-use helpers (cliclick on macOS, if archived)
|
|
245
|
-
copyComputerAssets(tmpDir, binDir)
|
|
246
|
-
|
|
247
|
-
// Cleanup
|
|
248
|
-
fs.rmSync(tmpDir, { recursive: true, force: true })
|
|
77
|
+
return target
|
|
249
78
|
}
|
|
250
79
|
|
|
251
80
|
// Copy bundled computer-use helper binaries (e.g. cliclick on macOS) from a
|
|
@@ -266,35 +95,64 @@ function copyComputerAssets(srcBinDir, dstBinDir) {
|
|
|
266
95
|
console.log("✓ Computer-use helpers copied from platform package")
|
|
267
96
|
}
|
|
268
97
|
|
|
269
|
-
|
|
270
|
-
|
|
98
|
+
// The binary ships in the platform-specific optionalDependency
|
|
99
|
+
// (@toothfairyai/tfcode-<platform>-<arch>[-baseline][-musl]). Link it into
|
|
100
|
+
// this package's bin/ dir and copy the web app + computer-use helpers.
|
|
101
|
+
function setupFromPlatformPackage() {
|
|
102
|
+
const { platform } = detectPlatform()
|
|
103
|
+
const pkgName = `@toothfairyai/${targetName()}`
|
|
271
104
|
const binaryName = platform === "windows" ? "tfcode.exe" : "tfcode"
|
|
272
|
-
const
|
|
105
|
+
const binDir = path.join(__dirname, "bin")
|
|
106
|
+
|
|
107
|
+
// ESM-compatible module resolution
|
|
108
|
+
let pkgDir
|
|
109
|
+
try {
|
|
110
|
+
const pkgUrl = import.meta.resolve(`${pkgName}/package.json`)
|
|
111
|
+
const pkgPath = fileURLToPath(pkgUrl)
|
|
112
|
+
pkgDir = path.dirname(pkgPath)
|
|
113
|
+
} catch (e) {
|
|
114
|
+
console.error(`Could not resolve ${pkgName}: ${e.message}`)
|
|
115
|
+
console.error("")
|
|
116
|
+
console.error("The platform package for your machine was not installed.")
|
|
117
|
+
console.error("")
|
|
118
|
+
console.error("Possible solutions:")
|
|
119
|
+
console.error(` 1. Install it explicitly: npm install ${pkgName}`)
|
|
120
|
+
console.error(" 2. Clear the npm cache and reinstall: npm cache clean --force && npm install @toothfairyai/tfcode")
|
|
121
|
+
console.error(" 3. Contact ToothFairyAI support for assistance")
|
|
122
|
+
console.error("")
|
|
123
|
+
process.exit(1)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const sourceBinary = path.join(pkgDir, "bin", binaryName)
|
|
127
|
+
if (!fs.existsSync(sourceBinary)) {
|
|
128
|
+
console.error(`Binary not found at ${sourceBinary}`)
|
|
129
|
+
process.exit(1)
|
|
130
|
+
}
|
|
273
131
|
|
|
132
|
+
// Link (or copy) the binary into bin/
|
|
274
133
|
if (!fs.existsSync(binDir)) fs.mkdirSync(binDir, { recursive: true })
|
|
134
|
+
const targetBinary = path.join(binDir, binaryName)
|
|
275
135
|
if (fs.existsSync(targetBinary)) fs.unlinkSync(targetBinary)
|
|
276
|
-
|
|
277
|
-
// Try hardlink, fall back to copy
|
|
278
136
|
try {
|
|
279
|
-
fs.linkSync(
|
|
137
|
+
fs.linkSync(sourceBinary, targetBinary)
|
|
280
138
|
} catch {
|
|
281
|
-
fs.copyFileSync(
|
|
139
|
+
fs.copyFileSync(sourceBinary, targetBinary)
|
|
282
140
|
}
|
|
283
|
-
|
|
284
141
|
fs.chmodSync(targetBinary, 0o755)
|
|
285
142
|
console.log(`tfcode installed to ${targetBinary}`)
|
|
286
143
|
|
|
287
|
-
//
|
|
288
|
-
const
|
|
289
|
-
const srcAppDir = path.join(sourceDir, "app")
|
|
144
|
+
// Copy app dir
|
|
145
|
+
const srcAppDir = path.join(pkgDir, "bin", "app")
|
|
290
146
|
const targetAppDir = path.join(binDir, "app")
|
|
291
147
|
if (fs.existsSync(path.join(srcAppDir, "dist", "index.html"))) {
|
|
292
148
|
if (fs.existsSync(targetAppDir)) fs.rmSync(targetAppDir, { recursive: true, force: true })
|
|
293
149
|
fs.cpSync(srcAppDir, targetAppDir, { recursive: true })
|
|
294
150
|
console.log("Web app copied from platform package")
|
|
151
|
+
} else {
|
|
152
|
+
console.log("! Web app not found in platform package")
|
|
295
153
|
}
|
|
296
|
-
|
|
297
|
-
copyComputerAssets(
|
|
154
|
+
|
|
155
|
+
copyComputerAssets(path.join(pkgDir, "bin"), binDir)
|
|
298
156
|
}
|
|
299
157
|
|
|
300
158
|
async function main() {
|
|
@@ -304,8 +162,8 @@ async function main() {
|
|
|
304
162
|
console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
|
|
305
163
|
console.log("")
|
|
306
164
|
|
|
307
|
-
//
|
|
308
|
-
|
|
165
|
+
// Setup binary from the npm platform package
|
|
166
|
+
setupFromPlatformPackage()
|
|
309
167
|
|
|
310
168
|
// Check for Python (needed for TF integration)
|
|
311
169
|
try {
|