@neyino/cli 0.0.0-stage → 0.3.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/npm/ensure.js ADDED
@@ -0,0 +1,97 @@
1
+ #!/usr/bin/env node
2
+ import childProcess from "node:child_process"
3
+ import fs from "node:fs"
4
+ import http from "node:http"
5
+ import https from "node:https"
6
+ import path from "node:path"
7
+ import { fileURLToPath } from "node:url"
8
+
9
+ export const pkgRoot = path.dirname(fileURLToPath(import.meta.url))
10
+ const MIN_BINARY_BYTES = 1_000_000
11
+ const base = () => (process.env.NEYINO_DOWNLOAD_BASE || "https://neyino.com/dl/latest").replace(/\/+$/, "")
12
+
13
+ const PLATFORM = { darwin: "darwin", linux: "linux", win32: "windows" }
14
+ const ARCH = { x64: "x64", arm64: "arm64" }
15
+
16
+ export function artifact() {
17
+ const platform = PLATFORM[process.platform]
18
+ const arch = ARCH[process.arch]
19
+ if (!platform || !arch) return undefined
20
+ return `neyino-${platform}-${arch}${platform === "windows" ? ".exe" : ""}`
21
+ }
22
+
23
+ export function binaryPath() {
24
+ return path.join(pkgRoot, process.platform === "win32" ? "neyino.exe" : "neyino")
25
+ }
26
+
27
+ function usable(binary) {
28
+ try {
29
+ if (!fs.existsSync(binary) || fs.statSync(binary).size < MIN_BINARY_BYTES) return false
30
+ return childProcess.spawnSync(binary, ["--version"], { stdio: "ignore", timeout: 20_000, windowsHide: true }).status === 0
31
+ } catch {
32
+ return false
33
+ }
34
+ }
35
+
36
+ export async function ensure(options) {
37
+ const quiet = options?.quiet === true
38
+ const name = artifact()
39
+ if (!name)
40
+ throw new Error(
41
+ `Unsupported platform ${process.platform}-${process.arch}. Supported: darwin/linux x64+arm64, windows x64+arm64.`,
42
+ )
43
+ const target = binaryPath()
44
+ if (usable(target)) return target
45
+ const url = `${base()}/${name}`
46
+ if (!quiet) console.error(`@neyino/cli: downloading binary from ${url}`)
47
+ await download(url, target)
48
+ if (!usable(target)) {
49
+ try {
50
+ fs.unlinkSync(target)
51
+ } catch {}
52
+ throw new Error(`Downloaded file from ${url} is not a working neyino binary for ${process.platform}-${process.arch}`)
53
+ }
54
+ return target
55
+ }
56
+
57
+ function download(url, target, redirects = 5) {
58
+ return new Promise((resolve, reject) => {
59
+ const lib = url.startsWith("http:") ? http : https
60
+ const request = lib.get(url, (response) => {
61
+ const status = response.statusCode ?? 0
62
+ if (status >= 300 && status < 400 && response.headers.location) {
63
+ response.resume()
64
+ if (redirects <= 0) return reject(new Error(`Too many redirects downloading ${url}`))
65
+ return resolve(download(new URL(response.headers.location, url).toString(), target, redirects - 1))
66
+ }
67
+ if (status !== 200) {
68
+ response.resume()
69
+ return reject(new Error(`Download failed: HTTP ${status} from ${url}`))
70
+ }
71
+ const length = Number(response.headers["content-length"] ?? 0)
72
+ if (length && length < MIN_BINARY_BYTES) {
73
+ response.resume()
74
+ return reject(new Error(`Download failed: only ${length} bytes from ${url} (expected a multi-megabyte binary)`))
75
+ }
76
+ const temp = `${target}.download-${process.pid}`
77
+ const file = fs.createWriteStream(temp, { mode: 0o755 })
78
+ response.pipe(file)
79
+ file.on("finish", () =>
80
+ file.close(() => {
81
+ const size = fs.statSync(temp).size
82
+ if (size < MIN_BINARY_BYTES) {
83
+ fs.unlinkSync(temp)
84
+ return reject(new Error(`Download failed: only ${size} bytes from ${url}`))
85
+ }
86
+ fs.renameSync(temp, target)
87
+ fs.chmodSync(target, 0o755)
88
+ resolve(undefined)
89
+ }),
90
+ )
91
+ file.on("error", reject)
92
+ response.on("error", reject)
93
+ })
94
+ request.on("error", (error) => reject(new Error(`Download failed from ${url}: ${error.message}`)))
95
+ request.setTimeout(300_000, () => request.destroy(new Error(`Download timed out from ${url}`)))
96
+ })
97
+ }
package/npm/neyino.js ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env node
2
+ import childProcess from "node:child_process"
3
+ import { ensure } from "./ensure.js"
4
+
5
+ const binary = await ensure()
6
+ const child = childProcess.spawn(binary, process.argv.slice(2), { stdio: "inherit" })
7
+ child.on("error", (error) => {
8
+ console.error(error.message)
9
+ process.exit(1)
10
+ })
11
+ for (const signal of ["SIGINT", "SIGTERM", "SIGHUP"]) process.on(signal, () => child.kill(signal))
12
+ child.on("exit", (code, signal) => {
13
+ if (signal) return process.kill(process.pid, signal)
14
+ process.exit(typeof code === "number" ? code : 0)
15
+ })
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env node
2
+ import { ensure } from "./ensure.js"
3
+
4
+ await ensure().catch((error) => {
5
+ console.error(`@neyino/cli postinstall: ${error instanceof Error ? error.message : String(error)}`)
6
+ console.error("The binary will be downloaded on the first run of `neyino` instead.")
7
+ })
package/package.json CHANGED
@@ -1,6 +1,37 @@
1
1
  {
2
+ "$schema": "https://json.schemastore.org/package.json",
2
3
  "name": "@neyino/cli",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
4
+ "version": "0.3.2",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "description": "Neyino CLI — agentic coding in your terminal. Installs the Neyino binary for your platform.",
8
+ "homepage": "https://neyino.com",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/Neyino/neyino.git",
12
+ "directory": "packages/cli"
13
+ },
14
+ "bugs": {
15
+ "url": "https://github.com/Neyino/neyino/issues"
16
+ },
17
+ "keywords": [
18
+ "neyino",
19
+ "ai",
20
+ "cli",
21
+ "agent",
22
+ "coding-agent",
23
+ "opencode"
24
+ ],
25
+ "bin": {
26
+ "neyino": "npm/neyino.js"
27
+ },
28
+ "files": [
29
+ "npm"
30
+ ],
31
+ "scripts": {
32
+ "postinstall": "node ./npm/postinstall.js"
33
+ },
34
+ "publishConfig": {
35
+ "access": "public"
36
+ }
37
+ }
package/README.md DELETED
@@ -1,3 +0,0 @@
1
- # Temporary Holding Version
2
-
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.