@logionsh/cli 0.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Logion contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,47 @@
1
+ # @logionsh/cli
2
+
3
+ Thin npm wrapper around the canonical [`logion-cli`](https://pypi.org/project/logion-cli/) Python package.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install -g @logionsh/cli
9
+ # or
10
+ npx @logionsh/cli --help
11
+ ```
12
+
13
+ ## Requirements
14
+
15
+ - Node ≥ 22
16
+ - Python ≥ 3.12 on the host (the wrapper auto-detects `python3` or `py`)
17
+ - One of `pipx`, `uv`, or the wrapper falls back to a managed venv
18
+
19
+ ## What this package does
20
+
21
+ On install, the postinstall hook:
22
+
23
+ 1. Finds a working Python 3.12+.
24
+ 2. Installs `logion-cli==<pinned-version>` from PyPI via `pipx`/`uv`/venv.
25
+ 3. Shims the `logion` and `lgn` binaries onto your PATH.
26
+
27
+ The pinned PyPI version is baked into the npm tarball at publish
28
+ time, so `npm install -g @logionsh/cli@0.3.0` always installs the
29
+ exact matching Python package.
30
+
31
+ ## Why not pure Node?
32
+
33
+ The canonical implementation is Python — the CLI is part of a
34
+ three-package Python workspace and shares code with the Python SDK.
35
+ A pure-Node port would double the codebase and drift from the source
36
+ of truth. This wrapper exists so JS-ecosystem users don't need to
37
+ think about Python package managers.
38
+
39
+ ## Environment variables
40
+
41
+ | Variable | Purpose |
42
+ | ---------------------------- | ---------------------------------------- |
43
+ | `LOGION_NPM_SKIP_INSTALL` | Set to `1` to skip postinstall (CI/test) |
44
+ | `LOGION_NPM_FORCE_INSTALLER` | Force `pipx`, `uv`, or `venv` |
45
+ | `LOGION_NPM_PYTHON` | Override Python binary path |
46
+
47
+ See https://github.com/nicolasmelo1/logion for the full project.
@@ -0,0 +1,74 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __toESM = (mod, isNodeMode, target2) => (target2 = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
18
+ // If the importer is in node compatibility mode or this is not an ESM
19
+ // file that has been converted to a CommonJS file using a Babel-
20
+ // compatible transform (i.e. "__esModule" has not been set), then set
21
+ // "default" to the CommonJS "module.exports" for node compatibility.
22
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target2, "default", { value: mod, enumerable: true }) : target2,
23
+ mod
24
+ ));
25
+
26
+ // src/bin/lgn.ts
27
+ var import_node_child_process = require("child_process");
28
+
29
+ // src/lib/which.ts
30
+ var import_node_fs = __toESM(require("fs"));
31
+ var import_node_path = __toESM(require("path"));
32
+ function candidatesForName(name) {
33
+ if (process.platform !== "win32") {
34
+ return [name];
35
+ }
36
+ const pathext = (process.env.PATHEXT ?? ".EXE;.CMD;.BAT;.COM").split(";").filter(Boolean);
37
+ if (import_node_path.default.extname(name)) {
38
+ return [name];
39
+ }
40
+ return [name, ...pathext.map((ext) => name + ext.toLowerCase())];
41
+ }
42
+ function which(name) {
43
+ const rawPath = process.env.PATH ?? "";
44
+ const dirs = rawPath.split(import_node_path.default.delimiter).filter(Boolean);
45
+ for (const dir of dirs) {
46
+ for (const candidate of candidatesForName(name)) {
47
+ const full = import_node_path.default.join(dir, candidate);
48
+ try {
49
+ const stat = import_node_fs.default.statSync(full);
50
+ if (stat.isFile()) {
51
+ return full;
52
+ }
53
+ } catch {
54
+ }
55
+ }
56
+ }
57
+ return null;
58
+ }
59
+
60
+ // src/bin/lgn.ts
61
+ var target = which("lgn") ?? which("logion");
62
+ if (!target) {
63
+ process.stderr.write(
64
+ "lgn/logion binary not found. Reinstall with `npm install -g @logionsh/cli` or install directly via `pipx install logion-cli`.\n"
65
+ );
66
+ process.exit(127);
67
+ }
68
+ var r = (0, import_node_child_process.spawnSync)(target, process.argv.slice(2), { stdio: "inherit" });
69
+ if (r.error) {
70
+ process.stderr.write(`${r.error.message}
71
+ `);
72
+ process.exit(1);
73
+ }
74
+ process.exit(r.status ?? 1);
@@ -0,0 +1,74 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __toESM = (mod, isNodeMode, target2) => (target2 = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
18
+ // If the importer is in node compatibility mode or this is not an ESM
19
+ // file that has been converted to a CommonJS file using a Babel-
20
+ // compatible transform (i.e. "__esModule" has not been set), then set
21
+ // "default" to the CommonJS "module.exports" for node compatibility.
22
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target2, "default", { value: mod, enumerable: true }) : target2,
23
+ mod
24
+ ));
25
+
26
+ // src/bin/logion.ts
27
+ var import_node_child_process = require("child_process");
28
+
29
+ // src/lib/which.ts
30
+ var import_node_fs = __toESM(require("fs"));
31
+ var import_node_path = __toESM(require("path"));
32
+ function candidatesForName(name) {
33
+ if (process.platform !== "win32") {
34
+ return [name];
35
+ }
36
+ const pathext = (process.env.PATHEXT ?? ".EXE;.CMD;.BAT;.COM").split(";").filter(Boolean);
37
+ if (import_node_path.default.extname(name)) {
38
+ return [name];
39
+ }
40
+ return [name, ...pathext.map((ext) => name + ext.toLowerCase())];
41
+ }
42
+ function which(name) {
43
+ const rawPath = process.env.PATH ?? "";
44
+ const dirs = rawPath.split(import_node_path.default.delimiter).filter(Boolean);
45
+ for (const dir of dirs) {
46
+ for (const candidate of candidatesForName(name)) {
47
+ const full = import_node_path.default.join(dir, candidate);
48
+ try {
49
+ const stat = import_node_fs.default.statSync(full);
50
+ if (stat.isFile()) {
51
+ return full;
52
+ }
53
+ } catch {
54
+ }
55
+ }
56
+ }
57
+ return null;
58
+ }
59
+
60
+ // src/bin/logion.ts
61
+ var target = which("logion");
62
+ if (!target) {
63
+ process.stderr.write(
64
+ "logion binary not found. Reinstall with `npm install -g @logionsh/cli` or install directly via `pipx install logion-cli`.\n"
65
+ );
66
+ process.exit(127);
67
+ }
68
+ var r = (0, import_node_child_process.spawnSync)(target, process.argv.slice(2), { stdio: "inherit" });
69
+ if (r.error) {
70
+ process.stderr.write(`${r.error.message}
71
+ `);
72
+ process.exit(1);
73
+ }
74
+ process.exit(r.status ?? 1);
@@ -0,0 +1,434 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __copyProps = (to, from, except, desc) => {
9
+ if (from && typeof from === "object" || typeof from === "function") {
10
+ for (let key of __getOwnPropNames(from))
11
+ if (!__hasOwnProp.call(to, key) && key !== except)
12
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
13
+ }
14
+ return to;
15
+ };
16
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
17
+ // If the importer is in node compatibility mode or this is not an ESM
18
+ // file that has been converted to a CommonJS file using a Babel-
19
+ // compatible transform (i.e. "__esModule" has not been set), then set
20
+ // "default" to the CommonJS "module.exports" for node compatibility.
21
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
22
+ mod
23
+ ));
24
+
25
+ // src/scripts/postinstall.ts
26
+ var import_node_child_process2 = require("child_process");
27
+ var import_node_fs3 = __toESM(require("fs"));
28
+ var import_node_os2 = __toESM(require("os"));
29
+ var import_node_path3 = __toESM(require("path"));
30
+
31
+ // src/lib/check-python.ts
32
+ var import_node_child_process = require("child_process");
33
+
34
+ // src/lib/which.ts
35
+ var import_node_fs = __toESM(require("fs"));
36
+ var import_node_path = __toESM(require("path"));
37
+ function candidatesForName(name) {
38
+ if (process.platform !== "win32") {
39
+ return [name];
40
+ }
41
+ const pathext = (process.env.PATHEXT ?? ".EXE;.CMD;.BAT;.COM").split(";").filter(Boolean);
42
+ if (import_node_path.default.extname(name)) {
43
+ return [name];
44
+ }
45
+ return [name, ...pathext.map((ext) => name + ext.toLowerCase())];
46
+ }
47
+ function which(name) {
48
+ const rawPath = process.env.PATH ?? "";
49
+ const dirs = rawPath.split(import_node_path.default.delimiter).filter(Boolean);
50
+ for (const dir of dirs) {
51
+ for (const candidate of candidatesForName(name)) {
52
+ const full = import_node_path.default.join(dir, candidate);
53
+ try {
54
+ const stat = import_node_fs.default.statSync(full);
55
+ if (stat.isFile()) {
56
+ return full;
57
+ }
58
+ } catch {
59
+ }
60
+ }
61
+ }
62
+ return null;
63
+ }
64
+
65
+ // src/lib/check-python.ts
66
+ var MIN_MAJOR = 3;
67
+ var MIN_MINOR = 12;
68
+ function candidates() {
69
+ if (process.platform === "win32") {
70
+ return [
71
+ { cmd: "py", args: ["-3"] },
72
+ { cmd: "python3", args: [] },
73
+ { cmd: "python", args: [] }
74
+ ];
75
+ }
76
+ return [
77
+ { cmd: "python3", args: [] },
78
+ { cmd: "python", args: [] }
79
+ ];
80
+ }
81
+ function probeVersion(cmd, args) {
82
+ const r = (0, import_node_child_process.spawnSync)(
83
+ cmd,
84
+ [...args, "-c", "import sys;print(sys.version_info[:2])"],
85
+ { encoding: "utf8", timeout: 5e3 }
86
+ );
87
+ if (r.status !== 0 || !r.stdout) {
88
+ return false;
89
+ }
90
+ const m = /\((\d+),\s*(\d+)\)/.exec(r.stdout);
91
+ if (!m) {
92
+ return false;
93
+ }
94
+ const major = Number(m[1]);
95
+ const minor = Number(m[2]);
96
+ if (major > MIN_MAJOR) {
97
+ return true;
98
+ }
99
+ return major === MIN_MAJOR && minor >= MIN_MINOR;
100
+ }
101
+ function detectPython() {
102
+ const override = process.env.LOGION_NPM_PYTHON;
103
+ if (override && override.trim().length > 0) {
104
+ return { cmd: override, args: [] };
105
+ }
106
+ for (const c of candidates()) {
107
+ const resolved = which(c.cmd);
108
+ if (!resolved) {
109
+ continue;
110
+ }
111
+ if (probeVersion(resolved, c.args)) {
112
+ return { cmd: resolved, args: c.args };
113
+ }
114
+ }
115
+ return null;
116
+ }
117
+
118
+ // src/lib/companion-bundle.ts
119
+ var import_node_crypto = __toESM(require("crypto"));
120
+ var import_node_fs2 = __toESM(require("fs"));
121
+ var import_node_os = __toESM(require("os"));
122
+ var import_node_path2 = __toESM(require("path"));
123
+ var LOGION_DIR = import_node_path2.default.join(import_node_os.default.homedir(), ".logion");
124
+ function byCodePoint(a, b) {
125
+ if (a < b) {
126
+ return -1;
127
+ }
128
+ if (a > b) {
129
+ return 1;
130
+ }
131
+ return 0;
132
+ }
133
+ function listBundleSourceEntries(sourceDir, log2) {
134
+ try {
135
+ return import_node_fs2.default.readdirSync(sourceDir).sort(byCodePoint);
136
+ } catch {
137
+ log2(
138
+ `LOGION_COMPANION_BUNDLE_SOURCE=${sourceDir} read failed \u2014 skipping companion bundle copy.`
139
+ );
140
+ return null;
141
+ }
142
+ }
143
+ function findCompanionTarball(sourceDir, entries, log2) {
144
+ for (const name of entries) {
145
+ if (!/^logion-marketplace-companion-.*\.tar\.gz$/.test(name)) {
146
+ continue;
147
+ }
148
+ const candidatePath = import_node_path2.default.join(sourceDir, name);
149
+ try {
150
+ if (import_node_fs2.default.statSync(candidatePath).isFile()) {
151
+ return { tarball: name, srcPath: candidatePath };
152
+ }
153
+ } catch {
154
+ log2(`Could not stat companion tarball candidate ${candidatePath}.`);
155
+ }
156
+ }
157
+ return null;
158
+ }
159
+ function sha256File(filePath) {
160
+ const hash = import_node_crypto.default.createHash("sha256");
161
+ const fd = import_node_fs2.default.openSync(filePath, "r");
162
+ const chunkSize = 64 * 1024;
163
+ const buf = Buffer.alloc(chunkSize);
164
+ let bytesRead;
165
+ try {
166
+ while ((bytesRead = import_node_fs2.default.readSync(fd, buf, 0, chunkSize, null)) > 0) {
167
+ hash.update(buf.subarray(0, bytesRead));
168
+ }
169
+ } finally {
170
+ import_node_fs2.default.closeSync(fd);
171
+ }
172
+ return hash.digest("hex");
173
+ }
174
+ function isBundleSourceDirectory(sourceDir, log2) {
175
+ if (!import_node_fs2.default.existsSync(sourceDir)) {
176
+ log2(
177
+ `LOGION_COMPANION_BUNDLE_SOURCE=${sourceDir} does not exist \u2014 skipping companion bundle copy.`
178
+ );
179
+ return false;
180
+ }
181
+ try {
182
+ if (import_node_fs2.default.statSync(sourceDir).isDirectory()) {
183
+ return true;
184
+ }
185
+ log2(
186
+ `LOGION_COMPANION_BUNDLE_SOURCE=${sourceDir} is not a directory \u2014 skipping companion bundle copy.`
187
+ );
188
+ } catch {
189
+ log2(
190
+ `LOGION_COMPANION_BUNDLE_SOURCE=${sourceDir} stat failed \u2014 skipping companion bundle copy.`
191
+ );
192
+ }
193
+ return false;
194
+ }
195
+ function resolveCompanionBundle(sourceDir, log2) {
196
+ if (!isBundleSourceDirectory(sourceDir, log2)) {
197
+ return null;
198
+ }
199
+ const entries = listBundleSourceEntries(sourceDir, log2);
200
+ if (!entries) {
201
+ return null;
202
+ }
203
+ const bundle = findCompanionTarball(sourceDir, entries, log2);
204
+ if (!bundle) {
205
+ log2(
206
+ `No companion tarball found in ${sourceDir} \u2014 skipping companion bundle copy.`
207
+ );
208
+ }
209
+ return bundle;
210
+ }
211
+ function copyCompanionBundle(bundle, log2) {
212
+ const envHome = process.env.LOGION_HOME;
213
+ const logionHome = envHome && envHome.length > 0 ? envHome : LOGION_DIR;
214
+ const bundlesDir = import_node_path2.default.join(logionHome, "companion-bundles");
215
+ import_node_fs2.default.mkdirSync(bundlesDir, { recursive: true });
216
+ const destPath = import_node_path2.default.join(bundlesDir, bundle.tarball);
217
+ import_node_fs2.default.copyFileSync(bundle.srcPath, destPath);
218
+ log2(`Copied companion bundle to ${destPath}`);
219
+ const sidecarName = bundle.tarball.replace(/\.tar\.gz$/, ".source.json");
220
+ const sidecarPath = import_node_path2.default.join(bundlesDir, sidecarName);
221
+ const sidecar = {
222
+ sourcePath: bundle.srcPath,
223
+ sha256: sha256File(destPath),
224
+ installedAt: (/* @__PURE__ */ new Date()).toISOString()
225
+ };
226
+ import_node_fs2.default.writeFileSync(sidecarPath, `${JSON.stringify(sidecar, null, 2)}
227
+ `);
228
+ log2(`Wrote companion marker ${sidecarPath}`);
229
+ }
230
+ function installCompanionBundle(log2) {
231
+ const sourceDir = process.env.LOGION_COMPANION_BUNDLE_SOURCE;
232
+ if (!sourceDir) {
233
+ return;
234
+ }
235
+ const bundle = resolveCompanionBundle(sourceDir, log2);
236
+ if (!bundle) {
237
+ return;
238
+ }
239
+ try {
240
+ copyCompanionBundle(bundle, log2);
241
+ } catch (error) {
242
+ const msg = error instanceof Error ? error.message : String(error);
243
+ log2(`Warning: companion bundle copy failed: ${msg}.`);
244
+ }
245
+ }
246
+
247
+ // src/scripts/postinstall.ts
248
+ var HOME = import_node_os2.default.homedir();
249
+ var LOGION_DIR2 = import_node_path3.default.join(HOME, ".logion");
250
+ var MANAGED_VENV_DIR = import_node_path3.default.join(LOGION_DIR2, "npm-managed-venv");
251
+ var MARKER_PATH = import_node_path3.default.join(LOGION_DIR2, "npm-wrapper-installer.json");
252
+ var LOCAL_BIN = import_node_path3.default.join(HOME, ".local", "bin");
253
+ var PLACEHOLDER_VERSION = "0.0.0-placeholder";
254
+ function log(msg) {
255
+ process.stderr.write(`[logion-postinstall] ${msg}
256
+ `);
257
+ }
258
+ function packageRoot() {
259
+ return import_node_path3.default.join(__dirname, "..", "..");
260
+ }
261
+ function getPinnedVersion() {
262
+ const pkgPath = import_node_path3.default.join(packageRoot(), "package.json");
263
+ const raw = import_node_fs3.default.readFileSync(pkgPath, "utf8");
264
+ const pkg = JSON.parse(raw);
265
+ if (pkg.logionCliVersion && pkg.logionCliVersion.length > 0) {
266
+ return pkg.logionCliVersion;
267
+ }
268
+ if (!pkg.version || pkg.version === PLACEHOLDER_VERSION) {
269
+ return null;
270
+ }
271
+ return pkg.version;
272
+ }
273
+ function writeMarker(installer, version) {
274
+ const marker = {
275
+ installer,
276
+ version,
277
+ installedAt: (/* @__PURE__ */ new Date()).toISOString()
278
+ };
279
+ import_node_fs3.default.mkdirSync(LOGION_DIR2, { recursive: true });
280
+ import_node_fs3.default.writeFileSync(MARKER_PATH, `${JSON.stringify(marker, null, 2)}
281
+ `);
282
+ }
283
+ function runChecked(file, args) {
284
+ const r = (0, import_node_child_process2.spawnSync)(file, args, { stdio: "inherit" });
285
+ if (r.error) {
286
+ throw r.error;
287
+ }
288
+ if (r.status !== 0) {
289
+ throw new Error(
290
+ `${file} ${args.join(" ")} exited with status ${String(r.status)}`
291
+ );
292
+ }
293
+ }
294
+ function installViaPipx(version) {
295
+ log(`Installing logion-cli==${version} via pipx...`);
296
+ runChecked("pipx", ["install", "--force", `logion-cli==${version}`]);
297
+ }
298
+ function installViaUv(version) {
299
+ log(`Installing logion-cli==${version} via uv tool...`);
300
+ runChecked("uv", [
301
+ "tool",
302
+ "install",
303
+ "--reinstall",
304
+ `logion-cli==${version}`
305
+ ]);
306
+ }
307
+ function venvBin(name) {
308
+ if (process.platform === "win32") {
309
+ return import_node_path3.default.join(MANAGED_VENV_DIR, "Scripts", `${name}.exe`);
310
+ }
311
+ return import_node_path3.default.join(MANAGED_VENV_DIR, "bin", name);
312
+ }
313
+ function linkOrCopy(src, dest) {
314
+ if (import_node_fs3.default.existsSync(dest)) {
315
+ import_node_fs3.default.unlinkSync(dest);
316
+ }
317
+ try {
318
+ import_node_fs3.default.symlinkSync(src, dest);
319
+ } catch {
320
+ import_node_fs3.default.copyFileSync(src, dest);
321
+ }
322
+ }
323
+ function shimNamesFor(base) {
324
+ return process.platform === "win32" ? [`${base}.exe`] : [base];
325
+ }
326
+ function installViaVenv(version, py) {
327
+ log(`Installing logion-cli==${version} via managed venv...`);
328
+ runChecked(py.cmd, [...py.args, "-m", "venv", MANAGED_VENV_DIR]);
329
+ runChecked(venvBin("pip"), ["install", `logion-cli==${version}`]);
330
+ import_node_fs3.default.mkdirSync(LOCAL_BIN, { recursive: true });
331
+ for (const base of ["logion", "lgn"]) {
332
+ const src = venvBin(base);
333
+ if (!import_node_fs3.default.existsSync(src)) {
334
+ continue;
335
+ }
336
+ for (const destName of shimNamesFor(base)) {
337
+ const dest = import_node_path3.default.join(LOCAL_BIN, destName);
338
+ try {
339
+ linkOrCopy(src, dest);
340
+ } catch (error) {
341
+ const msg = error instanceof Error ? error.message : String(error);
342
+ log(
343
+ `Warning: could not create shim ${dest}: ${msg}. Add ${LOCAL_BIN} to PATH manually.`
344
+ );
345
+ }
346
+ }
347
+ }
348
+ }
349
+ function verifyInstall(version) {
350
+ const target = which("logion");
351
+ if (!target) {
352
+ log(
353
+ "Warning: logion not on PATH yet. Ensure your installer's bin directory is exported."
354
+ );
355
+ return;
356
+ }
357
+ const r = (0, import_node_child_process2.spawnSync)(target, ["--version"], {
358
+ encoding: "utf8",
359
+ timeout: 1e4
360
+ });
361
+ if (r.status !== 0 || !r.stdout.includes(version)) {
362
+ log(
363
+ `Warning: installed version did not match requested ${version} (got: ${r.stdout.trim() || "<no output>"}).`
364
+ );
365
+ }
366
+ }
367
+ function pickInstaller(forced) {
368
+ if (forced === "pipx" || forced === "uv" || forced === "venv") {
369
+ return forced;
370
+ }
371
+ if (forced !== void 0 && forced.length > 0) {
372
+ return null;
373
+ }
374
+ if (which("pipx")) {
375
+ return "pipx";
376
+ }
377
+ if (which("uv")) {
378
+ return "uv";
379
+ }
380
+ return "venv";
381
+ }
382
+ function maybePrintOnboardingPointer() {
383
+ if (process.env.LOGION_NPM_SKIP_ONBOARDING === "1") {
384
+ return;
385
+ }
386
+ if (process.env.CI || process.env.LOGION_NONINTERACTIVE) {
387
+ return;
388
+ }
389
+ log("Next: run `logion onboarding` to set up your agent.");
390
+ }
391
+ function main() {
392
+ installCompanionBundle(log);
393
+ if (process.env.LOGION_NPM_SKIP_INSTALL === "1") {
394
+ log("LOGION_NPM_SKIP_INSTALL=1 \u2014 skipping CLI install.");
395
+ return;
396
+ }
397
+ const version = getPinnedVersion();
398
+ if (!version) {
399
+ log(
400
+ `No pinned version found (package.json still has ${PLACEHOLDER_VERSION}). Run \`node dist/scripts/version-from-manifest.js\` first or set LOGION_NPM_SKIP_INSTALL=1.`
401
+ );
402
+ process.exit(1);
403
+ }
404
+ const py = detectPython();
405
+ if (!py) {
406
+ log(
407
+ "Error: Python 3.12+ not found. Install Python 3.12+ or set LOGION_NPM_PYTHON."
408
+ );
409
+ log("See: https://www.python.org/downloads/");
410
+ log(
411
+ "Or set LOGION_NPM_SKIP_INSTALL=1 and install manually: pipx install logion-cli"
412
+ );
413
+ process.exit(1);
414
+ }
415
+ log(`Using Python: ${py.cmd} ${py.args.join(" ")}`.trim());
416
+ const forced = process.env.LOGION_NPM_FORCE_INSTALLER;
417
+ const installer = pickInstaller(forced);
418
+ if (!installer) {
419
+ log(`Error: unknown installer '${forced ?? ""}'. Use pipx, uv, or venv.`);
420
+ process.exit(1);
421
+ }
422
+ if (installer === "pipx") {
423
+ installViaPipx(version);
424
+ } else if (installer === "uv") {
425
+ installViaUv(version);
426
+ } else {
427
+ installViaVenv(version, py);
428
+ }
429
+ writeMarker(installer, version);
430
+ verifyInstall(version);
431
+ maybePrintOnboardingPointer();
432
+ log(`Installed logion-cli ${version} via ${installer}.`);
433
+ }
434
+ main();
@@ -0,0 +1,132 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __copyProps = (to, from, except, desc) => {
9
+ if (from && typeof from === "object" || typeof from === "function") {
10
+ for (let key of __getOwnPropNames(from))
11
+ if (!__hasOwnProp.call(to, key) && key !== except)
12
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
13
+ }
14
+ return to;
15
+ };
16
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
17
+ // If the importer is in node compatibility mode or this is not an ESM
18
+ // file that has been converted to a CommonJS file using a Babel-
19
+ // compatible transform (i.e. "__esModule" has not been set), then set
20
+ // "default" to the CommonJS "module.exports" for node compatibility.
21
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
22
+ mod
23
+ ));
24
+
25
+ // src/scripts/uninstall.ts
26
+ var import_node_child_process = require("child_process");
27
+ var import_node_fs2 = __toESM(require("fs"));
28
+ var import_node_os = __toESM(require("os"));
29
+ var import_node_path2 = __toESM(require("path"));
30
+
31
+ // src/lib/which.ts
32
+ var import_node_fs = __toESM(require("fs"));
33
+ var import_node_path = __toESM(require("path"));
34
+ function candidatesForName(name) {
35
+ if (process.platform !== "win32") {
36
+ return [name];
37
+ }
38
+ const pathext = (process.env.PATHEXT ?? ".EXE;.CMD;.BAT;.COM").split(";").filter(Boolean);
39
+ if (import_node_path.default.extname(name)) {
40
+ return [name];
41
+ }
42
+ return [name, ...pathext.map((ext) => name + ext.toLowerCase())];
43
+ }
44
+ function which(name) {
45
+ const rawPath = process.env.PATH ?? "";
46
+ const dirs = rawPath.split(import_node_path.default.delimiter).filter(Boolean);
47
+ for (const dir of dirs) {
48
+ for (const candidate of candidatesForName(name)) {
49
+ const full = import_node_path.default.join(dir, candidate);
50
+ try {
51
+ const stat = import_node_fs.default.statSync(full);
52
+ if (stat.isFile()) {
53
+ return full;
54
+ }
55
+ } catch {
56
+ }
57
+ }
58
+ }
59
+ return null;
60
+ }
61
+
62
+ // src/scripts/uninstall.ts
63
+ var HOME = import_node_os.default.homedir();
64
+ var LOGION_DIR = import_node_path2.default.join(HOME, ".logion");
65
+ var MANAGED_VENV_DIR = import_node_path2.default.join(LOGION_DIR, "npm-managed-venv");
66
+ var MARKER_PATH = import_node_path2.default.join(LOGION_DIR, "npm-wrapper-installer.json");
67
+ var LOCAL_BIN = import_node_path2.default.join(HOME, ".local", "bin");
68
+ function log(msg) {
69
+ process.stderr.write(`[logion-uninstall] ${msg}
70
+ `);
71
+ }
72
+ function readMarker() {
73
+ try {
74
+ const raw = import_node_fs2.default.readFileSync(MARKER_PATH, "utf8");
75
+ return JSON.parse(raw);
76
+ } catch {
77
+ return null;
78
+ }
79
+ }
80
+ function tryRun(file, args) {
81
+ const r = (0, import_node_child_process.spawnSync)(file, args, { encoding: "utf8", timeout: 1e4 });
82
+ if (r.status !== 0) {
83
+ log(`Warning: ${file} ${args.join(" ")} exited ${String(r.status)}.`);
84
+ }
85
+ }
86
+ function removeManagedVenv() {
87
+ if (!import_node_fs2.default.existsSync(MANAGED_VENV_DIR)) {
88
+ return;
89
+ }
90
+ try {
91
+ import_node_fs2.default.rmSync(MANAGED_VENV_DIR, { recursive: true, force: true });
92
+ log(`Removed managed venv at ${MANAGED_VENV_DIR}`);
93
+ } catch (error) {
94
+ const msg = error instanceof Error ? error.message : String(error);
95
+ log(`Warning: could not remove managed venv: ${msg}`);
96
+ }
97
+ }
98
+ function removeShims() {
99
+ const names = process.platform === "win32" ? ["logion.exe", "lgn.exe", "logion.cmd", "lgn.cmd"] : ["logion", "lgn"];
100
+ for (const name of names) {
101
+ const link = import_node_path2.default.join(LOCAL_BIN, name);
102
+ try {
103
+ const stat = import_node_fs2.default.lstatSync(link);
104
+ if (stat.isSymbolicLink() || stat.isFile()) {
105
+ import_node_fs2.default.unlinkSync(link);
106
+ }
107
+ } catch {
108
+ }
109
+ }
110
+ }
111
+ function main() {
112
+ const marker = readMarker();
113
+ if (!marker) {
114
+ removeShims();
115
+ return;
116
+ }
117
+ if (marker.installer === "venv") {
118
+ removeManagedVenv();
119
+ removeShims();
120
+ } else if (marker.installer === "pipx" && which("pipx")) {
121
+ log("Uninstalling logion-cli via pipx...");
122
+ tryRun("pipx", ["uninstall", "logion-cli"]);
123
+ } else if (marker.installer === "uv" && which("uv")) {
124
+ log("Uninstalling logion-cli via uv tool...");
125
+ tryRun("uv", ["tool", "uninstall", "logion-cli"]);
126
+ }
127
+ try {
128
+ import_node_fs2.default.unlinkSync(MARKER_PATH);
129
+ } catch {
130
+ }
131
+ }
132
+ main();
@@ -0,0 +1,63 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __copyProps = (to, from, except, desc) => {
9
+ if (from && typeof from === "object" || typeof from === "function") {
10
+ for (let key of __getOwnPropNames(from))
11
+ if (!__hasOwnProp.call(to, key) && key !== except)
12
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
13
+ }
14
+ return to;
15
+ };
16
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
17
+ // If the importer is in node compatibility mode or this is not an ESM
18
+ // file that has been converted to a CommonJS file using a Babel-
19
+ // compatible transform (i.e. "__esModule" has not been set), then set
20
+ // "default" to the CommonJS "module.exports" for node compatibility.
21
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
22
+ mod
23
+ ));
24
+
25
+ // src/scripts/version-from-manifest.ts
26
+ var import_node_fs = __toESM(require("fs"));
27
+ var import_node_path = __toESM(require("path"));
28
+ var PKG_DIR = import_node_path.default.join(__dirname, "..", "..");
29
+ var MANIFEST_PATH = import_node_path.default.join(
30
+ PKG_DIR,
31
+ "..",
32
+ "..",
33
+ "releases",
34
+ "manifest-stable.json"
35
+ );
36
+ function main() {
37
+ let manifest;
38
+ try {
39
+ manifest = JSON.parse(import_node_fs.default.readFileSync(MANIFEST_PATH, "utf8"));
40
+ } catch {
41
+ process.stderr.write(`Could not read manifest at ${MANIFEST_PATH}
42
+ `);
43
+ process.stderr.write("Run `make release-manifest` to generate it.\n");
44
+ process.exit(1);
45
+ }
46
+ const cliPkg = manifest.packages["logion-cli"];
47
+ if (!cliPkg?.version) {
48
+ process.stderr.write("Manifest does not contain logion-cli version.\n");
49
+ process.exit(1);
50
+ }
51
+ const version = cliPkg.version;
52
+ const pkgPath = import_node_path.default.join(PKG_DIR, "package.json");
53
+ const pkg = JSON.parse(import_node_fs.default.readFileSync(pkgPath, "utf8"));
54
+ pkg.version = version;
55
+ pkg.logionCliVersion = version;
56
+ import_node_fs.default.writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}
57
+ `);
58
+ process.stdout.write(
59
+ `Pinned logion-cli version to ${version} in package.json
60
+ `
61
+ );
62
+ }
63
+ main();
package/package.json ADDED
@@ -0,0 +1,70 @@
1
+ {
2
+ "name": "@logionsh/cli",
3
+ "version": "0.1.0",
4
+ "description": "Logion CLI — installs and shims the canonical Python logion-cli from PyPI.",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/nicolasmelo1/logion.git",
9
+ "directory": "packages/npm-wrapper"
10
+ },
11
+ "homepage": "https://github.com/nicolasmelo1/logion#readme",
12
+ "bugs": {
13
+ "url": "https://github.com/nicolasmelo1/logion/issues"
14
+ },
15
+ "keywords": [
16
+ "logion",
17
+ "cli",
18
+ "marketplace",
19
+ "agent"
20
+ ],
21
+ "bin": {
22
+ "logion": "dist/bin/logion.js",
23
+ "lgn": "dist/bin/lgn.js"
24
+ },
25
+ "scripts": {
26
+ "build": "tsup",
27
+ "prepublishOnly": "node dist/scripts/version-from-manifest.js",
28
+ "postinstall": "node dist/scripts/postinstall.js",
29
+ "preuninstall": "node dist/scripts/uninstall.js",
30
+ "pretest": "npm run build",
31
+ "test": "vitest run",
32
+ "test:watch": "vitest",
33
+ "typecheck": "tsc --noEmit",
34
+ "lint": "eslint .",
35
+ "lint:fix": "eslint . --fix",
36
+ "format": "prettier --write \"{src,test}/**/*.ts\" \"*.{ts,json,md}\"",
37
+ "format:check": "prettier --check \"{src,test}/**/*.ts\" \"*.{ts,json,md}\""
38
+ },
39
+ "engines": {
40
+ "node": ">=22"
41
+ },
42
+ "files": [
43
+ "dist/",
44
+ "README.md",
45
+ "LICENSE"
46
+ ],
47
+ "publishConfig": {
48
+ "access": "public",
49
+ "provenance": true
50
+ },
51
+ "devDependencies": {
52
+ "@eslint/js": "^9.18.0",
53
+ "@types/node": "^22.15.0",
54
+ "@vitest/eslint-plugin": "^1.1.25",
55
+ "eslint": "^9.18.0",
56
+ "eslint-config-prettier": "^9.1.0",
57
+ "eslint-import-resolver-typescript": "^4.4.5",
58
+ "eslint-plugin-import-x": "^4.6.1",
59
+ "eslint-plugin-n": "^17.15.1",
60
+ "eslint-plugin-promise": "^7.2.1",
61
+ "eslint-plugin-sonarjs": "^3.0.1",
62
+ "eslint-plugin-unicorn": "^56.0.1",
63
+ "prettier": "^3.4.2",
64
+ "tsup": "^8.4.0",
65
+ "typescript": "^5.8.0",
66
+ "typescript-eslint": "^8.20.0",
67
+ "vitest": "^3.2.0"
68
+ },
69
+ "logionCliVersion": "0.1.0"
70
+ }