zelari-code 1.0.2 → 1.1.0
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 +4 -1
- package/dist/cli/app.js +2 -1
- package/dist/cli/app.js.map +1 -1
- package/dist/cli/keyStore.js +1 -0
- package/dist/cli/keyStore.js.map +1 -1
- package/dist/cli/main.bundled.js +385 -72
- package/dist/cli/main.bundled.js.map +4 -4
- package/dist/cli/main.js +10 -0
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/modelDiscovery.js +2 -0
- package/dist/cli/modelDiscovery.js.map +1 -1
- package/dist/cli/modelPricing.js +4 -0
- package/dist/cli/modelPricing.js.map +1 -1
- package/dist/cli/provider/openai-compatible.js +3 -0
- package/dist/cli/provider/openai-compatible.js.map +1 -1
- package/dist/cli/providerConfig.js +1 -0
- package/dist/cli/providerConfig.js.map +1 -1
- package/dist/cli/slashCommands.js +2 -2
- package/dist/cli/slashCommands.js.map +1 -1
- package/dist/cli/slashHandlers/provider.js +3 -3
- package/dist/cli/slashHandlers/provider.js.map +1 -1
- package/dist/cli/slashHandlers/updater.js +77 -6
- package/dist/cli/slashHandlers/updater.js.map +1 -1
- package/dist/cli/utils/doctor.js +287 -0
- package/dist/cli/utils/doctor.js.map +1 -0
- package/package.json +4 -2
- package/scripts/postinstall.mjs +317 -0
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* doctor.ts — `zelari-code doctor` diagnostic.
|
|
3
|
+
*
|
|
4
|
+
* Run after a fresh install (or when something looks wrong) to check:
|
|
5
|
+
* 1. Bin shim health (does `zelari-code.cmd` / `zelari-code` exist
|
|
6
|
+
* in the npm global prefix, and does it reference the right
|
|
7
|
+
* package install?).
|
|
8
|
+
* 2. Node availability (`node` on PATH, version >= engines.node).
|
|
9
|
+
* 3. CLI bundle presence (did `npm run build:cli` produce
|
|
10
|
+
* `dist/cli/main.bundled.js`?).
|
|
11
|
+
* 4. Runtime deps (ink / react / zod / ink-text-input resolvable
|
|
12
|
+
* from the package root).
|
|
13
|
+
* 5. @zelari/core dependency (only checked if it appears in the
|
|
14
|
+
* package's deps or devDeps — it's a build-time dep for the
|
|
15
|
+
* workspace, and a runtime dep only for the source/tsx fallback
|
|
16
|
+
* path in bin/zelari-code.js).
|
|
17
|
+
* 6. PATH includes the npm global prefix.
|
|
18
|
+
*
|
|
19
|
+
* Output is human-readable, with one line per check + a trailing
|
|
20
|
+
* summary. Non-zero exit code if any critical check fails so this
|
|
21
|
+
* can be used in CI / install scripts.
|
|
22
|
+
*
|
|
23
|
+
* The function never throws: it catches all per-check errors and
|
|
24
|
+
* reports them as "FAIL" with the error message. The final return
|
|
25
|
+
* value tells the caller whether the install is healthy.
|
|
26
|
+
*
|
|
27
|
+
* @see scripts/postinstall.mjs — the install-time shim check
|
|
28
|
+
* @see src/cli/updater.ts — the self-update mechanism
|
|
29
|
+
*/
|
|
30
|
+
import { execSync } from "node:child_process";
|
|
31
|
+
import { existsSync, readFileSync, readlinkSync, statSync } from "node:fs";
|
|
32
|
+
import { createRequire } from "node:module";
|
|
33
|
+
import { fileURLToPath } from "node:url";
|
|
34
|
+
import path from "node:path";
|
|
35
|
+
const require = createRequire(import.meta.url);
|
|
36
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
37
|
+
// src/cli/utils/doctor.ts → <pkg>/src/cli/utils/doctor.ts
|
|
38
|
+
// We need <pkg> root. In dev (tsx) this is src/cli/utils/, in prod (bundled) it's dist/cli/utils/.
|
|
39
|
+
// In both cases we want the package root: that's `../../` from src/cli/utils, and `../../../` from dist/cli/utils/...
|
|
40
|
+
// Simplest: walk up until we find package.json with our name.
|
|
41
|
+
const packageRoot = findPackageRoot(__dirname);
|
|
42
|
+
function findPackageRoot(start) {
|
|
43
|
+
let dir = start;
|
|
44
|
+
// Walk at most 6 levels up (src/cli/utils -> src/cli -> src -> root, dist/cli/utils -> dist/cli -> dist -> root).
|
|
45
|
+
for (let i = 0; i < 6; i += 1) {
|
|
46
|
+
const candidate = path.join(dir, "package.json");
|
|
47
|
+
if (existsSync(candidate)) {
|
|
48
|
+
try {
|
|
49
|
+
const pkg = JSON.parse(readFileSync(candidate, "utf8"));
|
|
50
|
+
if (pkg.name === "zelari-code")
|
|
51
|
+
return dir;
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
// ignore
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
const parent = path.dirname(dir);
|
|
58
|
+
if (parent === dir)
|
|
59
|
+
break;
|
|
60
|
+
dir = parent;
|
|
61
|
+
}
|
|
62
|
+
// Fallback: assume standard layout.
|
|
63
|
+
return path.resolve(__dirname, "..", "..", "..");
|
|
64
|
+
}
|
|
65
|
+
const OK = (message) => ({
|
|
66
|
+
ok: true,
|
|
67
|
+
message,
|
|
68
|
+
severity: "critical",
|
|
69
|
+
});
|
|
70
|
+
const FAIL = (message, severity = "critical") => ({
|
|
71
|
+
ok: false,
|
|
72
|
+
message,
|
|
73
|
+
severity,
|
|
74
|
+
});
|
|
75
|
+
const WARN = (message) => ({
|
|
76
|
+
ok: false,
|
|
77
|
+
message,
|
|
78
|
+
severity: "warn",
|
|
79
|
+
});
|
|
80
|
+
/** Run a shell command and return trimmed stdout. Empty string on failure. */
|
|
81
|
+
function tryExec(cmd) {
|
|
82
|
+
try {
|
|
83
|
+
return execSync(cmd, {
|
|
84
|
+
encoding: "utf8",
|
|
85
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
86
|
+
}).trim();
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
return "";
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/** Read the installed package.json (for name + version + bin path). */
|
|
93
|
+
function readPackageJson() {
|
|
94
|
+
try {
|
|
95
|
+
const pkgPath = path.join(packageRoot, "package.json");
|
|
96
|
+
return JSON.parse(readFileSync(pkgPath, "utf8"));
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
/** Resolve the npm global prefix. Empty string on failure. */
|
|
103
|
+
function getGlobalPrefix() {
|
|
104
|
+
return ((process.env.npm_config_prefix ||
|
|
105
|
+
process.env.NPM_CONFIG_PREFIX ||
|
|
106
|
+
"").trim() || tryExec("npm prefix -g"));
|
|
107
|
+
}
|
|
108
|
+
/** Check the bin shim in the global prefix. */
|
|
109
|
+
function checkShim(pkgName) {
|
|
110
|
+
const prefix = getGlobalPrefix();
|
|
111
|
+
if (!prefix) {
|
|
112
|
+
return WARN("npm global prefix not detectable (run inside an npm context)");
|
|
113
|
+
}
|
|
114
|
+
const isWin = process.platform === "win32";
|
|
115
|
+
const shimName = isWin ? "zelari-code.cmd" : "zelari-code";
|
|
116
|
+
const shimPath = path.join(prefix, shimName);
|
|
117
|
+
if (!existsSync(shimPath)) {
|
|
118
|
+
return FAIL(`shim not found at ${shimPath}\n` +
|
|
119
|
+
` fix: npm install -g ${pkgName}@latest --force`);
|
|
120
|
+
}
|
|
121
|
+
try {
|
|
122
|
+
const st = statSync(shimPath);
|
|
123
|
+
if (isWin) {
|
|
124
|
+
const content = readFileSync(shimPath, "utf8");
|
|
125
|
+
if (content.includes(`${pkgName}\\bin\\`) ||
|
|
126
|
+
content.includes(`${pkgName}/bin/`)) {
|
|
127
|
+
return OK(`shim OK at ${shimPath} (${st.size} bytes)`);
|
|
128
|
+
}
|
|
129
|
+
return FAIL(`shim at ${shimPath} does not reference ${pkgName}/bin/\n` +
|
|
130
|
+
` fix: npm install -g ${pkgName}@latest --force`);
|
|
131
|
+
}
|
|
132
|
+
let target;
|
|
133
|
+
try {
|
|
134
|
+
target = readlinkSync(shimPath);
|
|
135
|
+
}
|
|
136
|
+
catch {
|
|
137
|
+
return FAIL(`shim at ${shimPath} is not a symlink (POSIX global installs should be symlinks)\n` +
|
|
138
|
+
` fix: npm install -g ${pkgName}@latest --force`);
|
|
139
|
+
}
|
|
140
|
+
const resolved = path.resolve(path.dirname(shimPath), target);
|
|
141
|
+
const expected = path.join(prefix, "node_modules", pkgName, "bin", "zelari-code.js");
|
|
142
|
+
if (resolved === expected) {
|
|
143
|
+
return OK(`shim OK at ${shimPath} → ${resolved}`);
|
|
144
|
+
}
|
|
145
|
+
return FAIL(`shim at ${shimPath} points to ${resolved}\n` +
|
|
146
|
+
` expected ${expected}\n` +
|
|
147
|
+
` fix: npm install -g ${pkgName}@latest --force`);
|
|
148
|
+
}
|
|
149
|
+
catch (err) {
|
|
150
|
+
return FAIL(`could not inspect shim: ${err instanceof Error ? err.message : String(err)}`);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
/** Check that node is on PATH and meets the engines requirement. */
|
|
154
|
+
function checkNode(pkg) {
|
|
155
|
+
const raw = tryExec("node --version");
|
|
156
|
+
if (!raw) {
|
|
157
|
+
return FAIL("`node` not found on PATH");
|
|
158
|
+
}
|
|
159
|
+
// raw is like "v20.11.1"
|
|
160
|
+
const m = raw.match(/^v?(\d+)\.(\d+)\.(\d+)/);
|
|
161
|
+
if (!m) {
|
|
162
|
+
return WARN(`could not parse node version: ${raw}`);
|
|
163
|
+
}
|
|
164
|
+
const major = Number(m[1]);
|
|
165
|
+
if (major < 20) {
|
|
166
|
+
return FAIL(`node ${raw} is older than the required engines.node (>= 20.0.0)`);
|
|
167
|
+
}
|
|
168
|
+
return OK(`node ${raw}`);
|
|
169
|
+
}
|
|
170
|
+
/** Check the CLI bundle exists. */
|
|
171
|
+
function checkBundle() {
|
|
172
|
+
const bundle = path.join(packageRoot, "dist", "cli", "main.bundled.js");
|
|
173
|
+
if (!existsSync(bundle)) {
|
|
174
|
+
return FAIL(`dist/cli/main.bundled.js missing at ${bundle}\n` +
|
|
175
|
+
` fix: npm run build:cli (then reinstall or run via tsx)`);
|
|
176
|
+
}
|
|
177
|
+
try {
|
|
178
|
+
const st = statSync(bundle);
|
|
179
|
+
return OK(`bundle OK (${(st.size / 1024 / 1024).toFixed(2)} MB)`);
|
|
180
|
+
}
|
|
181
|
+
catch (err) {
|
|
182
|
+
return FAIL(`could not stat bundle: ${err instanceof Error ? err.message : String(err)}`);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
/** Check each runtime dep is resolvable. */
|
|
186
|
+
function checkRuntimeDeps() {
|
|
187
|
+
const required = ["react", "react-dom", "ink", "ink-text-input", "zod"];
|
|
188
|
+
const missing = [];
|
|
189
|
+
for (const dep of required) {
|
|
190
|
+
try {
|
|
191
|
+
// createRequire from the package root — this is what `node bin/zelari-code.js` uses.
|
|
192
|
+
const localReq = createRequire(path.join(packageRoot, "package.json"));
|
|
193
|
+
localReq.resolve(dep);
|
|
194
|
+
}
|
|
195
|
+
catch {
|
|
196
|
+
missing.push(dep);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
if (missing.length === 0) {
|
|
200
|
+
return OK("runtime deps resolvable (react, react-dom, ink, ink-text-input, zod)");
|
|
201
|
+
}
|
|
202
|
+
return FAIL(`missing runtime deps: ${missing.join(", ")}\n` +
|
|
203
|
+
` fix: npm install -g ${"<pkg>"} --force (then reopen the terminal)`);
|
|
204
|
+
}
|
|
205
|
+
/** Check PATH includes the npm global prefix. */
|
|
206
|
+
function checkPath() {
|
|
207
|
+
const prefix = getGlobalPrefix();
|
|
208
|
+
if (!prefix)
|
|
209
|
+
return WARN("npm global prefix not detectable");
|
|
210
|
+
const pathSep = process.platform === "win32" ? ";" : ":";
|
|
211
|
+
const pathDirs = (process.env.PATH || "").split(pathSep);
|
|
212
|
+
if (pathDirs.includes(prefix)) {
|
|
213
|
+
return OK(`PATH includes npm prefix (${prefix})`);
|
|
214
|
+
}
|
|
215
|
+
return WARN(`PATH does not include npm prefix (${prefix})\n` +
|
|
216
|
+
` symptom: "zelari-code: command not found" after install\n` +
|
|
217
|
+
` fix (POSIX): export PATH="$(npm prefix -g)/bin:$PATH"\n` +
|
|
218
|
+
` fix (Windows): $env:Path = "$(npm prefix -g);$env:Path"`);
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Run all checks and print results. Returns true if install is healthy
|
|
222
|
+
* (no critical failures), false otherwise. Never throws.
|
|
223
|
+
*/
|
|
224
|
+
export function runDoctor() {
|
|
225
|
+
const pkg = readPackageJson();
|
|
226
|
+
const pkgName = pkg?.name ?? "zelari-code";
|
|
227
|
+
const checks = [
|
|
228
|
+
{ name: "node", run: () => checkNode(pkg) },
|
|
229
|
+
{ name: "bin shim", run: () => checkShim(pkgName) },
|
|
230
|
+
{ name: "cli bundle", run: () => checkBundle() },
|
|
231
|
+
{ name: "runtime deps", run: () => checkRuntimeDeps() },
|
|
232
|
+
{ name: "PATH", run: () => checkPath() },
|
|
233
|
+
];
|
|
234
|
+
// eslint-disable-next-line no-console
|
|
235
|
+
console.log(`zelari-code doctor (v${pkg?.version ?? "unknown"})`);
|
|
236
|
+
// eslint-disable-next-line no-console
|
|
237
|
+
console.log("platform:", process.platform, process.arch);
|
|
238
|
+
// eslint-disable-next-line no-console
|
|
239
|
+
console.log("node: ", process.version);
|
|
240
|
+
// eslint-disable-next-line no-console
|
|
241
|
+
console.log("prefix: ", getGlobalPrefix() || "(unknown)");
|
|
242
|
+
// eslint-disable-next-line no-console
|
|
243
|
+
console.log("root: ", packageRoot);
|
|
244
|
+
// eslint-disable-next-line no-console
|
|
245
|
+
console.log("");
|
|
246
|
+
let criticalFails = 0;
|
|
247
|
+
let warns = 0;
|
|
248
|
+
for (const c of checks) {
|
|
249
|
+
let result;
|
|
250
|
+
try {
|
|
251
|
+
result = c.run();
|
|
252
|
+
}
|
|
253
|
+
catch (err) {
|
|
254
|
+
result = FAIL(`unexpected error: ${err instanceof Error ? err.message : String(err)}`);
|
|
255
|
+
}
|
|
256
|
+
const tag = result.ok
|
|
257
|
+
? "OK "
|
|
258
|
+
: result.severity === "critical"
|
|
259
|
+
? "FAIL"
|
|
260
|
+
: "WARN";
|
|
261
|
+
const color = result.ok
|
|
262
|
+
? "\x1b[32m"
|
|
263
|
+
: result.severity === "critical"
|
|
264
|
+
? "\x1b[31m"
|
|
265
|
+
: "\x1b[33m";
|
|
266
|
+
const reset = "\x1b[0m";
|
|
267
|
+
// eslint-disable-next-line no-console
|
|
268
|
+
console.log(` ${color}${tag}${reset} ${c.name.padEnd(14)}${result.message.replace(/\n/g, "\n ")}`);
|
|
269
|
+
if (!result.ok) {
|
|
270
|
+
if (result.severity === "critical")
|
|
271
|
+
criticalFails += 1;
|
|
272
|
+
else
|
|
273
|
+
warns += 1;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
// eslint-disable-next-line no-console
|
|
277
|
+
console.log("");
|
|
278
|
+
if (criticalFails === 0 && warns === 0) {
|
|
279
|
+
// eslint-disable-next-line no-console
|
|
280
|
+
console.log("\x1b[32m✔ all checks passed\x1b[0m");
|
|
281
|
+
return true;
|
|
282
|
+
}
|
|
283
|
+
// eslint-disable-next-line no-console
|
|
284
|
+
console.log(`✗ ${criticalFails} critical failure(s), ${warns} warning(s)`);
|
|
285
|
+
return criticalFails === 0;
|
|
286
|
+
}
|
|
287
|
+
//# sourceMappingURL=doctor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../../src/cli/utils/doctor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC3E,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC/C,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAC/D,0DAA0D;AAC1D,mGAAmG;AACnG,sHAAsH;AACtH,8DAA8D;AAC9D,MAAM,WAAW,GAAG,eAAe,CAAC,SAAS,CAAC,CAAC;AAE/C,SAAS,eAAe,CAAC,KAAa;IACpC,IAAI,GAAG,GAAG,KAAK,CAAC;IAChB,kHAAkH;IAClH,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QAC9B,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC;QACjD,IAAI,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;YAC1B,IAAI,CAAC;gBACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,SAAS,EAAE,MAAM,CAAC,CAErD,CAAC;gBACF,IAAI,GAAG,CAAC,IAAI,KAAK,aAAa;oBAAE,OAAO,GAAG,CAAC;YAC7C,CAAC;YAAC,MAAM,CAAC;gBACP,SAAS;YACX,CAAC;QACH,CAAC;QACD,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACjC,IAAI,MAAM,KAAK,GAAG;YAAE,MAAM;QAC1B,GAAG,GAAG,MAAM,CAAC;IACf,CAAC;IACD,oCAAoC;IACpC,OAAO,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;AACnD,CAAC;AASD,MAAM,EAAE,GAAG,CAAC,OAAe,EAAe,EAAE,CAAC,CAAC;IAC5C,EAAE,EAAE,IAAI;IACR,OAAO;IACP,QAAQ,EAAE,UAAU;CACrB,CAAC,CAAC;AACH,MAAM,IAAI,GAAG,CACX,OAAe,EACf,WAAgC,UAAU,EAC7B,EAAE,CAAC,CAAC;IACjB,EAAE,EAAE,KAAK;IACT,OAAO;IACP,QAAQ;CACT,CAAC,CAAC;AACH,MAAM,IAAI,GAAG,CAAC,OAAe,EAAe,EAAE,CAAC,CAAC;IAC9C,EAAE,EAAE,KAAK;IACT,OAAO;IACP,QAAQ,EAAE,MAAM;CACjB,CAAC,CAAC;AAEH,8EAA8E;AAC9E,SAAS,OAAO,CAAC,GAAW;IAC1B,IAAI,CAAC;QACH,OAAO,QAAQ,CAAC,GAAG,EAAE;YACnB,QAAQ,EAAE,MAAM;YAChB,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC;SACpC,CAAC,CAAC,IAAI,EAAE,CAAC;IACZ,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,uEAAuE;AACvE,SAAS,eAAe;IAKtB,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,cAAc,CAAC,CAAC;QACvD,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,OAAO,EAAE,MAAM,CAAC,CAI9C,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,8DAA8D;AAC9D,SAAS,eAAe;IACtB,OAAO,CACL,CACE,OAAO,CAAC,GAAG,CAAC,iBAAiB;QAC7B,OAAO,CAAC,GAAG,CAAC,iBAAiB;QAC7B,EAAE,CACH,CAAC,IAAI,EAAE,IAAI,OAAO,CAAC,eAAe,CAAC,CACrC,CAAC;AACJ,CAAC;AAED,+CAA+C;AAC/C,SAAS,SAAS,CAAC,OAAe;IAChC,MAAM,MAAM,GAAG,eAAe,EAAE,CAAC;IACjC,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,IAAI,CAAC,8DAA8D,CAAC,CAAC;IAC9E,CAAC;IACD,MAAM,KAAK,GAAG,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC;IAC3C,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,aAAa,CAAC;IAC3D,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IAC7C,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC1B,OAAO,IAAI,CACT,qBAAqB,QAAQ,IAAI;YAC/B,iCAAiC,OAAO,iBAAiB,CAC5D,CAAC;IACJ,CAAC;IACD,IAAI,CAAC;QACH,MAAM,EAAE,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAAC;QAC9B,IAAI,KAAK,EAAE,CAAC;YACV,MAAM,OAAO,GAAG,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;YAC/C,IACE,OAAO,CAAC,QAAQ,CAAC,GAAG,OAAO,SAAS,CAAC;gBACrC,OAAO,CAAC,QAAQ,CAAC,GAAG,OAAO,OAAO,CAAC,EACnC,CAAC;gBACD,OAAO,EAAE,CAAC,cAAc,QAAQ,KAAK,EAAE,CAAC,IAAI,SAAS,CAAC,CAAC;YACzD,CAAC;YACD,OAAO,IAAI,CACT,WAAW,QAAQ,uBAAuB,OAAO,SAAS;gBACxD,iCAAiC,OAAO,iBAAiB,CAC5D,CAAC;QACJ,CAAC;QACD,IAAI,MAAc,CAAC;QACnB,IAAI,CAAC;YACH,MAAM,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;QAClC,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CACT,WAAW,QAAQ,gEAAgE;gBACjF,iCAAiC,OAAO,iBAAiB,CAC5D,CAAC;QACJ,CAAC;QACD,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,CAAC;QAC9D,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CACxB,MAAM,EACN,cAAc,EACd,OAAO,EACP,KAAK,EACL,gBAAgB,CACjB,CAAC;QACF,IAAI,QAAQ,KAAK,QAAQ,EAAE,CAAC;YAC1B,OAAO,EAAE,CAAC,cAAc,QAAQ,MAAM,QAAQ,EAAE,CAAC,CAAC;QACpD,CAAC;QACD,OAAO,IAAI,CACT,WAAW,QAAQ,cAAc,QAAQ,IAAI;YAC3C,qBAAqB,QAAQ,IAAI;YACjC,iCAAiC,OAAO,iBAAiB,CAC5D,CAAC;IACJ,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,IAAI,CACT,2BAA2B,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAC9E,CAAC;IACJ,CAAC;AACH,CAAC;AAED,oEAAoE;AACpE,SAAS,SAAS,CAChB,GAKQ;IAER,MAAM,GAAG,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;IACtC,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,OAAO,IAAI,CAAC,0BAA0B,CAAC,CAAC;IAC1C,CAAC;IACD,yBAAyB;IACzB,MAAM,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC,wBAAwB,CAAC,CAAC;IAC9C,IAAI,CAAC,CAAC,EAAE,CAAC;QACP,OAAO,IAAI,CAAC,iCAAiC,GAAG,EAAE,CAAC,CAAC;IACtD,CAAC;IACD,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3B,IAAI,KAAK,GAAG,EAAE,EAAE,CAAC;QACf,OAAO,IAAI,CACT,QAAQ,GAAG,sDAAsD,CAClE,CAAC;IACJ,CAAC;IACD,OAAO,EAAE,CAAC,QAAQ,GAAG,EAAE,CAAC,CAAC;AAC3B,CAAC;AAED,mCAAmC;AACnC,SAAS,WAAW;IAClB,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,iBAAiB,CAAC,CAAC;IACxE,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;QACxB,OAAO,IAAI,CACT,uCAAuC,MAAM,IAAI;YAC/C,oEAAoE,CACvE,CAAC;IACJ,CAAC;IACD,IAAI,CAAC;QACH,MAAM,EAAE,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC;QAC5B,OAAO,EAAE,CAAC,cAAc,CAAC,EAAE,CAAC,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;IACpE,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,IAAI,CACT,0BAA0B,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAC7E,CAAC;IACJ,CAAC;AACH,CAAC;AAED,4CAA4C;AAC5C,SAAS,gBAAgB;IACvB,MAAM,QAAQ,GAAG,CAAC,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,gBAAgB,EAAE,KAAK,CAAC,CAAC;IACxE,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,KAAK,MAAM,GAAG,IAAI,QAAQ,EAAE,CAAC;QAC3B,IAAI,CAAC;YACH,qFAAqF;YACrF,MAAM,QAAQ,GAAG,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,cAAc,CAAC,CAAC,CAAC;YACvE,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACxB,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACpB,CAAC;IACH,CAAC;IACD,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,EAAE,CACP,sEAAsE,CACvE,CAAC;IACJ,CAAC;IACD,OAAO,IAAI,CACT,yBAAyB,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI;QAC7C,iCAAiC,OAAO,wCAAwC,CACnF,CAAC;AACJ,CAAC;AAED,iDAAiD;AACjD,SAAS,SAAS;IAChB,MAAM,MAAM,GAAG,eAAe,EAAE,CAAC;IACjC,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC,kCAAkC,CAAC,CAAC;IAC7D,MAAM,OAAO,GAAG,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;IACzD,MAAM,QAAQ,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACzD,IAAI,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;QAC9B,OAAO,EAAE,CAAC,6BAA6B,MAAM,GAAG,CAAC,CAAC;IACpD,CAAC;IACD,OAAO,IAAI,CACT,qCAAqC,MAAM,KAAK;QAC9C,oEAAoE;QACpE,oEAAoE;QACpE,kEAAkE,CACrE,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,SAAS;IACvB,MAAM,GAAG,GAAG,eAAe,EAAE,CAAC;IAC9B,MAAM,OAAO,GAAG,GAAG,EAAE,IAAI,IAAI,aAAa,CAAC;IAE3C,MAAM,MAAM,GAAoD;QAC9D,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE;QAC3C,EAAE,IAAI,EAAE,UAAU,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE;QACnD,EAAE,IAAI,EAAE,YAAY,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,WAAW,EAAE,EAAE;QAChD,EAAE,IAAI,EAAE,cAAc,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,gBAAgB,EAAE,EAAE;QACvD,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,SAAS,EAAE,EAAE;KACzC,CAAC;IAEF,sCAAsC;IACtC,OAAO,CAAC,GAAG,CAAC,wBAAwB,GAAG,EAAE,OAAO,IAAI,SAAS,GAAG,CAAC,CAAC;IAClE,sCAAsC;IACtC,OAAO,CAAC,GAAG,CAAC,WAAW,EAAE,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IACzD,sCAAsC;IACtC,OAAO,CAAC,GAAG,CAAC,WAAW,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;IAC1C,sCAAsC;IACtC,OAAO,CAAC,GAAG,CAAC,WAAW,EAAE,eAAe,EAAE,IAAI,WAAW,CAAC,CAAC;IAC3D,sCAAsC;IACtC,OAAO,CAAC,GAAG,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;IACtC,sCAAsC;IACtC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAEhB,IAAI,aAAa,GAAG,CAAC,CAAC;IACtB,IAAI,KAAK,GAAG,CAAC,CAAC;IAEd,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;QACvB,IAAI,MAAmB,CAAC;QACxB,IAAI,CAAC;YACH,MAAM,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC;QACnB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,GAAG,IAAI,CACX,qBAAqB,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CACxE,CAAC;QACJ,CAAC;QACD,MAAM,GAAG,GAAG,MAAM,CAAC,EAAE;YACnB,CAAC,CAAC,MAAM;YACR,CAAC,CAAC,MAAM,CAAC,QAAQ,KAAK,UAAU;gBAC9B,CAAC,CAAC,MAAM;gBACR,CAAC,CAAC,MAAM,CAAC;QACb,MAAM,KAAK,GAAG,MAAM,CAAC,EAAE;YACrB,CAAC,CAAC,UAAU;YACZ,CAAC,CAAC,MAAM,CAAC,QAAQ,KAAK,UAAU;gBAC9B,CAAC,CAAC,UAAU;gBACZ,CAAC,CAAC,UAAU,CAAC;QACjB,MAAM,KAAK,GAAG,SAAS,CAAC;QACxB,sCAAsC;QACtC,OAAO,CAAC,GAAG,CACT,KAAK,KAAK,GAAG,GAAG,GAAG,KAAK,KAAK,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,kBAAkB,CAAC,EAAE,CACrG,CAAC;QACF,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;YACf,IAAI,MAAM,CAAC,QAAQ,KAAK,UAAU;gBAAE,aAAa,IAAI,CAAC,CAAC;;gBAClD,KAAK,IAAI,CAAC,CAAC;QAClB,CAAC;IACH,CAAC;IAED,sCAAsC;IACtC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,IAAI,aAAa,KAAK,CAAC,IAAI,KAAK,KAAK,CAAC,EAAE,CAAC;QACvC,sCAAsC;QACtC,OAAO,CAAC,GAAG,CAAC,oCAAoC,CAAC,CAAC;QAClD,OAAO,IAAI,CAAC;IACd,CAAC;IACD,sCAAsC;IACtC,OAAO,CAAC,GAAG,CAAC,KAAK,aAAa,yBAAyB,KAAK,aAAa,CAAC,CAAC;IAC3E,OAAO,aAAa,KAAK,CAAC,CAAC;AAC7B,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zelari-code",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Zelari Code — AI Council coding agent CLI. Multi-agent orchestration (Caronte, Nettuno, Gerione, Plutone, Minosse, Lucifero) with slash commands, provider-agnostic LLM streaming, and self-update.",
|
|
5
5
|
"author": "N-THEM Studio",
|
|
6
6
|
"license": "SEE LICENSE IN LICENSE",
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
"files": [
|
|
16
16
|
"bin",
|
|
17
17
|
"dist",
|
|
18
|
+
"scripts/postinstall.mjs",
|
|
18
19
|
"LICENSE",
|
|
19
20
|
"README.md",
|
|
20
21
|
"package.json"
|
|
@@ -32,6 +33,7 @@
|
|
|
32
33
|
"test:watch": "vitest",
|
|
33
34
|
"smoke": "node bin/zelari-code.js --version",
|
|
34
35
|
"verify:council": "npm run build --workspace=@zelari/core && node scripts/verify-council.mjs",
|
|
36
|
+
"postinstall": "node scripts/postinstall.mjs",
|
|
35
37
|
"prepublishOnly": "node scripts/prepublish.mjs"
|
|
36
38
|
},
|
|
37
39
|
"dependencies": {
|
|
@@ -42,7 +44,7 @@
|
|
|
42
44
|
},
|
|
43
45
|
"devDependencies": {
|
|
44
46
|
"@testing-library/react": "^16.3.2",
|
|
45
|
-
"@zelari/core": "1.0
|
|
47
|
+
"@zelari/core": "1.1.0",
|
|
46
48
|
"@types/node": "^25.3.0",
|
|
47
49
|
"@types/react": "^19.0.10",
|
|
48
50
|
"esbuild": "^0.25.0",
|
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* postinstall.mjs — Shim health check for global installs.
|
|
4
|
+
*
|
|
5
|
+
* Why this exists:
|
|
6
|
+
* On Windows + npm 10/11, `npm install -g zelari-code@<x>` sometimes
|
|
7
|
+
* fails to (re)create the bin shim `zelari-code.cmd` after an upgrade
|
|
8
|
+
* (e.g. 0.7.x → 1.0.x). The package is correctly installed in
|
|
9
|
+
* `<prefix>/node_modules/zelari-code/`, but `<prefix>/zelari-code.cmd`
|
|
10
|
+
* is missing, so `zelari-code` returns "command not found" on the
|
|
11
|
+
* next shell open. This script runs as `postinstall` and:
|
|
12
|
+
*
|
|
13
|
+
* 1. Detects global installs only (skips local installs to avoid noise).
|
|
14
|
+
* 2. Verifies the expected shim path exists and is current.
|
|
15
|
+
* 3. If missing or stale, prints a clear, actionable warning with the
|
|
16
|
+
* exact fix command. It does NOT auto-repair the shim — auto-repair
|
|
17
|
+
* is risky (shim is a privileged file in the global prefix and a
|
|
18
|
+
* misdirected symlink can shadow other tools).
|
|
19
|
+
* 4. Also warns if the npm global prefix is not on the current PATH,
|
|
20
|
+
* which is a separate-but-related failure mode (npm install
|
|
21
|
+
* succeeds, the shim exists, but the user can't run the command
|
|
22
|
+
* because the prefix isn't reachable from their shell).
|
|
23
|
+
*
|
|
24
|
+
* All output goes to stderr so it doesn't pollute the install log on
|
|
25
|
+
* success. The script is fail-safe: any error here is swallowed and
|
|
26
|
+
* logged, never thrown — a broken postinstall must NEVER fail the
|
|
27
|
+
* install itself.
|
|
28
|
+
*
|
|
29
|
+
* @see scripts/diagnose-path.ps1 — interactive Windows diagnostic
|
|
30
|
+
* @see scripts/fix-path.ps1 — adds npm prefix to user PATH
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
import { execSync } from 'node:child_process';
|
|
34
|
+
import {
|
|
35
|
+
existsSync,
|
|
36
|
+
readFileSync,
|
|
37
|
+
readlinkSync,
|
|
38
|
+
statSync,
|
|
39
|
+
writeFileSync,
|
|
40
|
+
} from 'node:fs';
|
|
41
|
+
import { fileURLToPath } from 'node:url';
|
|
42
|
+
import path from 'node:path';
|
|
43
|
+
|
|
44
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
45
|
+
const pkgRoot = path.resolve(__dirname, '..');
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Auto-repair a MISSING Windows bin shim.
|
|
49
|
+
*
|
|
50
|
+
* This is the single most common "zelari-code: command not found on some
|
|
51
|
+
* Windows PCs" failure: npm correctly unpacks the package into
|
|
52
|
+
* `<prefix>\node_modules\zelari-code\` but never (re)creates the three bin
|
|
53
|
+
* shims `<prefix>\zelari-code[.cmd|.ps1]`, so nothing on PATH resolves the
|
|
54
|
+
* command. Repairing this is safe — unlike overwriting a shim that points
|
|
55
|
+
* elsewhere (which could shadow another tool, hence why we never touch an
|
|
56
|
+
* existing shim), writing a shim under OUR own bin name, pointing at OUR
|
|
57
|
+
* own installed package, only fills a gap npm left behind.
|
|
58
|
+
*
|
|
59
|
+
* We write the exact same three files npm's own `cmd-shim` produces (cmd,
|
|
60
|
+
* PowerShell, and a POSIX sh wrapper for Git Bash/MSYS), each resolving to
|
|
61
|
+
* `%~dp0\node_modules\zelari-code\bin\zelari-code.js` relative to the shim
|
|
62
|
+
* dir — which is the global prefix, exactly where the package was unpacked.
|
|
63
|
+
*
|
|
64
|
+
* Opt out with `ZELARI_NO_SHIM_REPAIR=1`. Never throws; returns true only
|
|
65
|
+
* when at least the `.cmd` shim was written.
|
|
66
|
+
*
|
|
67
|
+
* @param {string} prefix npm global prefix (dir that holds the shims)
|
|
68
|
+
* @param {string} pkgName installed package name
|
|
69
|
+
* @returns {boolean} true if the .cmd shim was created
|
|
70
|
+
*/
|
|
71
|
+
function repairWindowsShim(prefix, pkgName) {
|
|
72
|
+
if (process.env.ZELARI_NO_SHIM_REPAIR === '1') return false;
|
|
73
|
+
const rel = `node_modules\\${pkgName}\\bin\\zelari-code.js`;
|
|
74
|
+
const relPosix = `node_modules/${pkgName}/bin/zelari-code.js`;
|
|
75
|
+
|
|
76
|
+
const cmd = [
|
|
77
|
+
'@ECHO off',
|
|
78
|
+
'GOTO start',
|
|
79
|
+
':find_dp0',
|
|
80
|
+
'SET dp0=%~dp0',
|
|
81
|
+
'EXIT /b',
|
|
82
|
+
':start',
|
|
83
|
+
'SETLOCAL',
|
|
84
|
+
'CALL :find_dp0',
|
|
85
|
+
'',
|
|
86
|
+
'IF EXIST "%dp0%\\node.exe" (',
|
|
87
|
+
' SET "_prog=%dp0%\\node.exe"',
|
|
88
|
+
') ELSE (',
|
|
89
|
+
' SET "_prog=node"',
|
|
90
|
+
' SET PATHEXT=%PATHEXT:;.JS;=;%',
|
|
91
|
+
')',
|
|
92
|
+
'',
|
|
93
|
+
`endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\\${rel}" %*`,
|
|
94
|
+
'',
|
|
95
|
+
].join('\r\n');
|
|
96
|
+
|
|
97
|
+
const ps1 = [
|
|
98
|
+
'#!/usr/bin/env pwsh',
|
|
99
|
+
'$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent',
|
|
100
|
+
'',
|
|
101
|
+
'$exe=""',
|
|
102
|
+
'if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {',
|
|
103
|
+
' $exe=".exe"',
|
|
104
|
+
'}',
|
|
105
|
+
'$ret=0',
|
|
106
|
+
'if (Test-Path "$basedir/node$exe") {',
|
|
107
|
+
' if ($MyInvocation.ExpectingInput) {',
|
|
108
|
+
` $input | & "$basedir/node$exe" "$basedir/${relPosix}" $args`,
|
|
109
|
+
' } else {',
|
|
110
|
+
` & "$basedir/node$exe" "$basedir/${relPosix}" $args`,
|
|
111
|
+
' }',
|
|
112
|
+
' $ret=$LASTEXITCODE',
|
|
113
|
+
'} else {',
|
|
114
|
+
' if ($MyInvocation.ExpectingInput) {',
|
|
115
|
+
` $input | & "node$exe" "$basedir/${relPosix}" $args`,
|
|
116
|
+
' } else {',
|
|
117
|
+
` & "node$exe" "$basedir/${relPosix}" $args`,
|
|
118
|
+
' }',
|
|
119
|
+
' $ret=$LASTEXITCODE',
|
|
120
|
+
'}',
|
|
121
|
+
'exit $ret',
|
|
122
|
+
'',
|
|
123
|
+
].join('\n');
|
|
124
|
+
|
|
125
|
+
const sh = [
|
|
126
|
+
'#!/bin/sh',
|
|
127
|
+
'basedir=$(dirname "$(echo "$0" | sed -e \'s,\\\\,/,g\')")',
|
|
128
|
+
'',
|
|
129
|
+
'case `uname` in',
|
|
130
|
+
' *CYGWIN*|*MINGW*|*MSYS*)',
|
|
131
|
+
' if command -v cygpath > /dev/null 2>&1; then',
|
|
132
|
+
' basedir=`cygpath -w "$basedir"`',
|
|
133
|
+
' fi',
|
|
134
|
+
' ;;',
|
|
135
|
+
'esac',
|
|
136
|
+
'',
|
|
137
|
+
'if [ -x "$basedir/node" ]; then',
|
|
138
|
+
` exec "$basedir/node" "$basedir/${relPosix}" "$@"`,
|
|
139
|
+
'else',
|
|
140
|
+
` exec node "$basedir/${relPosix}" "$@"`,
|
|
141
|
+
'fi',
|
|
142
|
+
'',
|
|
143
|
+
].join('\n');
|
|
144
|
+
|
|
145
|
+
const targets = [
|
|
146
|
+
['zelari-code.cmd', cmd],
|
|
147
|
+
['zelari-code.ps1', ps1],
|
|
148
|
+
['zelari-code', sh],
|
|
149
|
+
];
|
|
150
|
+
let cmdWritten = false;
|
|
151
|
+
for (const [name, content] of targets) {
|
|
152
|
+
const dest = path.join(prefix, name);
|
|
153
|
+
// Never clobber an existing shim — only fill the gap npm left.
|
|
154
|
+
if (existsSync(dest)) continue;
|
|
155
|
+
try {
|
|
156
|
+
writeFileSync(dest, content, 'utf8');
|
|
157
|
+
if (name === 'zelari-code.cmd') cmdWritten = true;
|
|
158
|
+
} catch {
|
|
159
|
+
// Permission denied (prefix owned by admin) or read-only FS — the
|
|
160
|
+
// warning below still tells the user how to fix it manually.
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return cmdWritten;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const warn = (msg) => {
|
|
167
|
+
// eslint-disable-next-line no-console
|
|
168
|
+
console.warn(`[zelari-code postinstall] ${msg}`);
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
const note = (msg) => {
|
|
172
|
+
// eslint-disable-next-line no-console
|
|
173
|
+
console.warn(`[zelari-code postinstall] ${msg}`);
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
try {
|
|
177
|
+
// 0. Only run for global installs. Local installs (`npm install zelari-code`)
|
|
178
|
+
// create a `node_modules/.bin/zelari-code` symlink that npm handles
|
|
179
|
+
// correctly, and the user runs the command via `npx` or package.json
|
|
180
|
+
// scripts — so the global-prefix shim is irrelevant.
|
|
181
|
+
//
|
|
182
|
+
// npm sets `npm_config_global=true` for `npm install -g` and for
|
|
183
|
+
// `npm install -g <pkg>` invocations. Some npm versions also set
|
|
184
|
+
// `npm_install_global` — check both for safety.
|
|
185
|
+
const isGlobal =
|
|
186
|
+
process.env.npm_config_global === 'true' ||
|
|
187
|
+
process.env.npm_install_global === 'true' ||
|
|
188
|
+
process.env.NPM_CONFIG_GLOBAL === 'true';
|
|
189
|
+
|
|
190
|
+
if (!isGlobal) {
|
|
191
|
+
process.exit(0);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// 1. Resolve the global prefix. Prefer the env var (set by npm), fall
|
|
195
|
+
// back to `npm prefix -g`. If both fail, bail silently — we can't
|
|
196
|
+
// diagnose without knowing where to look.
|
|
197
|
+
let prefix = (process.env.npm_config_prefix || process.env.NPM_CONFIG_PREFIX || '').trim();
|
|
198
|
+
if (!prefix) {
|
|
199
|
+
try {
|
|
200
|
+
prefix = execSync('npm prefix -g', { encoding: 'utf8' }).trim();
|
|
201
|
+
} catch {
|
|
202
|
+
// npm not on PATH, or this is a weirdly-sandboxed install.
|
|
203
|
+
// Either way, we can't help further.
|
|
204
|
+
process.exit(0);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const pkgName = process.env.npm_package_name || 'zelari-code';
|
|
209
|
+
const pkgVersion = process.env.npm_package_version || 'unknown';
|
|
210
|
+
const expectedBinScript = path.join(pkgRoot, 'bin', 'zelari-code.js');
|
|
211
|
+
const isWin = process.platform === 'win32';
|
|
212
|
+
const shimName = isWin ? 'zelari-code.cmd' : 'zelari-code';
|
|
213
|
+
const shimPath = path.join(prefix, shimName);
|
|
214
|
+
const expectedNodeModulesShim = isWin
|
|
215
|
+
? `%~dp0\\node_modules\\${pkgName}\\bin\\zelari-code.js`
|
|
216
|
+
: path.join(prefix, 'node_modules', pkgName, 'bin', 'zelari-code.js');
|
|
217
|
+
|
|
218
|
+
// 2. Check the shim.
|
|
219
|
+
let shimOk = false;
|
|
220
|
+
let shimReason = '';
|
|
221
|
+
|
|
222
|
+
if (!existsSync(shimPath)) {
|
|
223
|
+
shimReason = `shim file not found at ${shimPath}`;
|
|
224
|
+
// Auto-repair the most common Windows failure: the package unpacked but
|
|
225
|
+
// npm never created the bin shim. We only write shims that are MISSING,
|
|
226
|
+
// and only under our own bin name → no risk of shadowing another tool.
|
|
227
|
+
if (isWin) {
|
|
228
|
+
const repaired = repairWindowsShim(prefix, pkgName);
|
|
229
|
+
if (repaired && existsSync(shimPath)) {
|
|
230
|
+
note(`shim was missing — auto-created ${shimPath}`);
|
|
231
|
+
note('open a NEW terminal and run `zelari-code --version` to confirm.');
|
|
232
|
+
process.exit(0);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
} else {
|
|
236
|
+
try {
|
|
237
|
+
const st = statSync(shimPath);
|
|
238
|
+
if (isWin) {
|
|
239
|
+
// Windows: .cmd shim. Validate the content references the current
|
|
240
|
+
// package name + bin path. The exact text varies slightly across
|
|
241
|
+
// npm versions, so we just check for the package name + the
|
|
242
|
+
// bin/zelari-code.js substring.
|
|
243
|
+
const content = readFileSync(shimPath, 'utf8');
|
|
244
|
+
if (content.includes(`${pkgName}\\bin\\zelari-code.js`) || content.includes(`${pkgName}/bin/zelari-code.js`)) {
|
|
245
|
+
shimOk = true;
|
|
246
|
+
} else {
|
|
247
|
+
shimReason = `shim at ${shimPath} does not reference ${pkgName}/bin/zelari-code.js — it may belong to a different install`;
|
|
248
|
+
}
|
|
249
|
+
} else {
|
|
250
|
+
// POSIX: symlink (or hardlink). readlinkSync throws for non-symlinks.
|
|
251
|
+
let target;
|
|
252
|
+
try {
|
|
253
|
+
target = readlinkSync(shimPath);
|
|
254
|
+
} catch {
|
|
255
|
+
// Not a symlink — could be a hardlink or a copied file. In
|
|
256
|
+
// either case, npm shouldn't have done that. Treat as broken.
|
|
257
|
+
shimReason = `shim at ${shimPath} is not a symlink (npm should create a symlink for global installs on POSIX)`;
|
|
258
|
+
shimOk = false;
|
|
259
|
+
}
|
|
260
|
+
if (target) {
|
|
261
|
+
const resolved = path.resolve(path.dirname(shimPath), target);
|
|
262
|
+
if (resolved === expectedNodeModulesShim) {
|
|
263
|
+
shimOk = true;
|
|
264
|
+
} else {
|
|
265
|
+
shimReason = `shim at ${shimPath} points to ${resolved}, expected ${expectedNodeModulesShim}`;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
// If we reach here with shimOk still false and shimReason still empty,
|
|
270
|
+
// it's a non-symlink non-cmd file — leave shimReason empty so the
|
|
271
|
+
// generic message is shown.
|
|
272
|
+
if (!shimOk && !shimReason) {
|
|
273
|
+
shimReason = `shim at ${shimPath} is not a regular file or symlink (mode=${st.mode})`;
|
|
274
|
+
}
|
|
275
|
+
} catch (err) {
|
|
276
|
+
shimReason = `could not inspect shim at ${shimPath}: ${err.message}`;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
if (shimOk) {
|
|
281
|
+
note(`shim OK: ${shimPath}`);
|
|
282
|
+
process.exit(0);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// 3. Shim is broken or missing. Emit a clear, actionable warning.
|
|
286
|
+
warn('==============================================================');
|
|
287
|
+
warn(' npm install completed, but the `zelari-code` shim is broken.');
|
|
288
|
+
warn('==============================================================');
|
|
289
|
+
warn(` Reason: ${shimReason}`);
|
|
290
|
+
warn('');
|
|
291
|
+
warn(' The package itself is correctly installed in:');
|
|
292
|
+
warn(` ${path.join(prefix, 'node_modules', pkgName)}`);
|
|
293
|
+
warn(' But the bin shim is not where npm should have created it:');
|
|
294
|
+
warn(` ${shimPath}`);
|
|
295
|
+
warn('');
|
|
296
|
+
warn(' Symptom: `zelari-code` returns "command not found" in your');
|
|
297
|
+
warn(' shell, even though `npm ls -g` lists the package.');
|
|
298
|
+
warn('');
|
|
299
|
+
warn(' Fix (try in order, run as Administrator on Windows if needed):');
|
|
300
|
+
warn('');
|
|
301
|
+
warn(` npm install -g ${pkgName}@${pkgVersion} --force`);
|
|
302
|
+
warn('');
|
|
303
|
+
warn(' If the command is still not found after that, your npm');
|
|
304
|
+
warn(' global prefix may not be on your shell PATH. Run:');
|
|
305
|
+
warn('');
|
|
306
|
+
warn(' npm bin -g # shows the expected shim dir');
|
|
307
|
+
warn(' echo "$PATH" # check if it is included');
|
|
308
|
+
warn('');
|
|
309
|
+
warn(' On Windows, scripts/diagnose-path.ps1 and scripts/fix-path.ps1');
|
|
310
|
+
warn(' in the repo can inspect and repair PATH for you.');
|
|
311
|
+
warn('==============================================================');
|
|
312
|
+
process.exit(0); // Never fail the install.
|
|
313
|
+
} catch (err) {
|
|
314
|
+
// Last-resort safety net: log and exit 0.
|
|
315
|
+
warn(`unexpected error (install is not affected): ${err.message}`);
|
|
316
|
+
process.exit(0);
|
|
317
|
+
}
|