@qialike/cli 0.8.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.
Files changed (3) hide show
  1. package/README.md +44 -0
  2. package/bin/qialike.js +169 -0
  3. package/package.json +27 -0
package/README.md ADDED
@@ -0,0 +1,44 @@
1
+ # @qialike/cli
2
+
3
+ Terminal client for **DeepSeek Harness** — a full-screen TUI over an agent session.
4
+
5
+ > **This npm channel ships the Windows binaries only.** Linux and macOS install with the shell
6
+ > installer, because a `curl … | bash` line is what those platforms already have:
7
+ >
8
+ > ```sh
9
+ > curl -fsSL https://qialike.com/install | bash
10
+ > ```
11
+ >
12
+ > Windows has no `bash`, which is why this package exists: it turns the manual
13
+ > "download a zip, unpack it into `%USERPROFILE%\.dsh\bin\`, edit your PATH, reopen the terminal"
14
+ > sequence into one command.
15
+
16
+ ## Install (Windows)
17
+
18
+ ```sh
19
+ npm i -g @qialike/cli
20
+ qialike --version
21
+ ```
22
+
23
+ **You do not choose an architecture.** The package declares one optional dependency per Windows
24
+ target — `@qialike/cli-win32-x64` and `@qialike/cli-win32-arm64` — each restricted with `os`/`cpu`,
25
+ so npm downloads only the payload that matches your machine (x64 or arm64), never both.
26
+
27
+ ## When optional dependencies are skipped
28
+
29
+ `--omit=optional`, pnpm's defaults (which skip them), a mirror that dropped the platform package or
30
+ an interrupted install all leave the launcher without a binary. It then prints the exact command to
31
+ install the package for your architecture, for example:
32
+
33
+ ```sh
34
+ npm i -g @qialike/cli-win32-arm64@0.8.1
35
+ ```
36
+
37
+ ## Alternative installers
38
+
39
+ - Shell installer (Linux, macOS): `curl -fsSL https://qialike.com/install | bash`
40
+ - Release archives: <https://github.com/qialike/qialike/releases>
41
+
42
+ ## License
43
+
44
+ MIT
package/bin/qialike.js ADDED
@@ -0,0 +1,169 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * The `qialike` npm entry point: locate the Windows binary for this machine's
4
+ * architecture and hand over.
5
+ *
6
+ * WHY THIS FILE EXISTS. qialike is a compiled single-file executable, not a Node
7
+ * program, but npm is how Windows users install command-line tools — that
8
+ * platform has no `bash`, so the documented `curl … | bash` installer cannot run
9
+ * there and the README would have to walk people through downloading a zip,
10
+ * unpacking it by hand and editing their PATH. This wrapper is what turns that
11
+ * into `npm i -g @qialike/cli`.
12
+ *
13
+ * HOW THE ARCHITECTURE IS CHOSEN. The parent package declares one
14
+ * `optionalDependencies` entry per Windows architecture, and each of those
15
+ * packages declares the `os`/`cpu` pair it can run on. npm installs only the
16
+ * matching one, so an x64 machine downloads `@qialike/cli-win32-x64` and an
17
+ * arm64 machine downloads `@qialike/cli-win32-arm64` — no prompt, no detection
18
+ * code of our own, and never both payloads. That mechanism is load-bearing:
19
+ * this file is the part that reports when it breaks.
20
+ *
21
+ * WHY A WRAPPER AT ALL, rather than pointing `bin` straight at `qialike.exe`:
22
+ * npm's `cmd-shim` does support a shebang-less target ("assume it's something
23
+ * that'll be compiled … just call it directly"), but no widely used package
24
+ * relies on that path — esbuild, Biome, SWC and sharp all ship a small script
25
+ * in front of the binary, and the reason is the failure mode below: an
26
+ * `optionalDependency` can go missing (a mirrored registry that dropped it,
27
+ * `--omit=optional`, pnpm's defaults, a partial install), and only a wrapper can
28
+ * say so in words. A bare exec would fail with whatever the shell prints for a
29
+ * missing file.
30
+ *
31
+ * @module @qialike/cli/bin
32
+ */
33
+
34
+ 'use strict'
35
+
36
+ const { spawnSync } = require('node:child_process')
37
+ const { existsSync, readFileSync } = require('node:fs')
38
+ const { dirname, join } = require('node:path')
39
+
40
+ /** The installer this npm channel stands in for, quoted in every message. */
41
+ const SHELL_INSTALLER = 'curl -fsSL https://qialike.com/install | bash'
42
+ const RELEASES = 'https://github.com/qialike/qialike/releases'
43
+
44
+ /**
45
+ * Supported targets, and the binary name inside each platform package. The keys
46
+ * are `<process.platform>-<process.arch>`; the payload keeps its `.exe` because
47
+ * that is the file the build produces.
48
+ * @type {Record<string, string>}
49
+ */
50
+ const TARGETS = {
51
+ 'win32-x64': 'qialike.exe',
52
+ 'win32-arm64': 'qialike.exe',
53
+ }
54
+
55
+ const key = `${process.platform}-${process.arch}`
56
+ const binaryName = TARGETS[key]
57
+
58
+ /**
59
+ * The version of the parent package, read from its own `package.json`. Used
60
+ * only to write an exact reinstall command: a bare `npm i -g <name>` could
61
+ * fetch a platform package that does not match the parent that resolved it.
62
+ * @returns {string} the semver string, or `latest` when it cannot be read.
63
+ */
64
+ function ownVersion() {
65
+ try {
66
+ const manifest = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf8'))
67
+ return typeof manifest.version === 'string' && manifest.version.length > 0 ? manifest.version : 'latest'
68
+ } catch {
69
+ return 'latest'
70
+ }
71
+ }
72
+
73
+ /**
74
+ * Report a fatal setup problem and stop, without pretending the binary ran.
75
+ * The exit code is always 1, never the binary's own — a wrapper failure is not
76
+ * a model turn failing.
77
+ * @param {string} message - the full, already-wrapped diagnostic.
78
+ * @returns {never} always exits.
79
+ */
80
+ function fail(message) {
81
+ process.stderr.write(`${message}\n`)
82
+ process.exit(1)
83
+ }
84
+
85
+ // This channel carries Windows binaries only. Refuse loudly and name the route
86
+ // that does work for the platform we are on: silently doing nothing, or dying
87
+ // with a spawn error, would both be worse than one actionable sentence.
88
+ if (process.platform !== 'win32') {
89
+ fail(
90
+ `qialike: the npm channel ships Windows binaries only (this is ${key}).\n`
91
+ + '\n'
92
+ + ' Install it with the shell installer instead:\n'
93
+ + ` ${SHELL_INSTALLER}\n`
94
+ + '\n'
95
+ + ' That is what Linux and macOS use; "npm i -g @qialike/cli" exists because\n'
96
+ + ' Windows has no bash to run the installer with.\n',
97
+ )
98
+ }
99
+
100
+ // x64 and arm64 are the only Windows targets the release publishes, and npm
101
+ // picks between them with `cpu`. Anything else (ia32, or a future arch) has no
102
+ // package to resolve.
103
+ if (!binaryName) {
104
+ fail(
105
+ `qialike: no npm build for ${key}.\n`
106
+ + '\n'
107
+ + ` Published Windows targets: ${Object.keys(TARGETS).join(', ')}\n`
108
+ + '\n'
109
+ + ' For anything else, build from source or fetch a release archive:\n'
110
+ + ` ${RELEASES}\n`,
111
+ )
112
+ }
113
+
114
+ const name = `@qialike/cli-${key}`
115
+
116
+ // Resolve the platform package through Node itself, so the same relative layout
117
+ // works for a global install (%APPDATA%\npm\node_modules\…), a local install and
118
+ // a workspace. `package.json` is used purely as a landmark: it keeps resolving
119
+ // even for a package that declares `exports`.
120
+ let manifestPath
121
+ try {
122
+ manifestPath = require.resolve(`${name}/package.json`)
123
+ } catch (error) {
124
+ const reason = error instanceof Error ? error.message : String(error)
125
+ const skipped = process.env.npm_config_optional === 'false'
126
+ fail(
127
+ `qialike: the ${name} package is missing, so there is no binary to run.\n`
128
+ + '\n'
129
+ + ' This is expected when npm was told to skip optional dependencies\n'
130
+ + ` (${skipped ? 'npm_config_optional=false is set right now' : '--omit=optional / --no-optional'}),\n`
131
+ + ' and possible with pnpm (which skips them by default), when a mirror\n'
132
+ + ' dropped the package, or when the install was interrupted.\n'
133
+ + '\n'
134
+ + ' Install it explicitly:\n'
135
+ + ` npm i -g ${name}@${ownVersion()}\n`
136
+ + '\n'
137
+ + ` (resolution error: ${reason})\n`,
138
+ )
139
+ }
140
+
141
+ const exe = join(dirname(manifestPath), binaryName)
142
+ if (!existsSync(exe)) {
143
+ fail(
144
+ `qialike: ${name} is installed but carries no ${binaryName}.\n`
145
+ + '\n'
146
+ + ` Expected at: ${exe}\n`
147
+ + ` Reinstall: npm i -g ${name}@${ownVersion()}\n`,
148
+ )
149
+ }
150
+
151
+ // Hand the terminal over: stdio is inherited, because the TUI owns raw mode,
152
+ // colour and resize. The child's exit code is what the caller sees.
153
+ const result = spawnSync(exe, process.argv.slice(2), { stdio: 'inherit' })
154
+
155
+ if (result.error) {
156
+ const reason = result.error instanceof Error ? result.error.message : String(result.error)
157
+ fail(
158
+ `qialike: failed to launch ${exe}\n`
159
+ + '\n'
160
+ + ` ${reason}\n`
161
+ + '\n'
162
+ + ' If the file exists and this persists the download may be incomplete —\n'
163
+ + ` reinstall: npm i -g ${name}@${ownVersion()}\n`,
164
+ )
165
+ }
166
+
167
+ // A signal-terminated child has `status === null`; count that as a failure
168
+ // rather than as success, because the turn did not complete.
169
+ process.exit(result.status === null ? 1 : result.status)
package/package.json ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "@qialike/cli",
3
+ "version": "0.8.1",
4
+ "description": "Terminal client for DeepSeek Harness. Installs the Windows binary for your architecture (x64 or arm64) automatically.",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/qialike/qialike.git"
9
+ },
10
+ "homepage": "https://qialike.com",
11
+ "bin": {
12
+ "qialike": "bin/qialike.js"
13
+ },
14
+ "files": [
15
+ "bin/qialike.js"
16
+ ],
17
+ "engines": {
18
+ "node": ">=18"
19
+ },
20
+ "optionalDependencies": {
21
+ "@qialike/cli-win32-x64": "0.8.1",
22
+ "@qialike/cli-win32-arm64": "0.8.1"
23
+ },
24
+ "publishConfig": {
25
+ "access": "public"
26
+ }
27
+ }