@moneysiren/app 0.1.0-alpha.10 → 0.1.0-alpha.11

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 CHANGED
@@ -17,6 +17,15 @@ The package installs both commands:
17
17
  - `moneysiren`
18
18
  - `msiren`
19
19
 
20
+ If npm reports `EEXIST` for `moneysiren` or `msiren`, an older global MoneySiren install left command shims behind. Remove the old global packages and reinstall:
21
+
22
+ ```powershell
23
+ npm uninstall -g @moneysiren/cli @moneysiren/app
24
+ npm install -g @moneysiren/app@alpha --force
25
+ ```
26
+
27
+ The app package also removes stale MoneySiren-owned command shims during global install when npm exposes the global prefix.
28
+
20
29
  ## What It Installs
21
30
 
22
31
  - CLI command surface.
@@ -1,6 +1,6 @@
1
1
  import type { InstallSurface } from "./install-profile.js";
2
2
  export declare const DEFAULT_RELEASE_REPOSITORY = "ztwz11/moneysiren";
3
- export declare const DEFAULT_RELEASE_TAG = "v0.1.0-alpha.10";
3
+ export declare const DEFAULT_RELEASE_TAG = "v0.1.0-alpha.11";
4
4
  export interface ReleaseInstallOptions {
5
5
  env?: Record<string, string | undefined>;
6
6
  fetchImpl: typeof fetch;
@@ -7,7 +7,7 @@ import { promisify } from "node:util";
7
7
  const execFileAsync = promisify(execFile);
8
8
  export const DEFAULT_RELEASE_REPOSITORY = "ztwz11/moneysiren";
9
9
  // Keep the source-free installer pinned to the latest published desktop/web release tag.
10
- export const DEFAULT_RELEASE_TAG = "v0.1.0-alpha.10";
10
+ export const DEFAULT_RELEASE_TAG = "v0.1.0-alpha.11";
11
11
  const RELEASE_REPOSITORY_ENV_KEY = "MONEYSIREN_RELEASE_REPOSITORY";
12
12
  const RELEASE_TAG_ENV_KEY = "MONEYSIREN_RELEASE_TAG";
13
13
  const RELEASE_INSTALL_DIR_ENV_KEY = "MONEYSIREN_RELEASE_INSTALL_DIR";
@@ -1,2 +1,2 @@
1
- export declare const CLI_VERSION = "0.1.0-alpha.10";
1
+ export declare const CLI_VERSION = "0.1.0-alpha.11";
2
2
  //# sourceMappingURL=version.d.ts.map
@@ -1,2 +1,2 @@
1
- export const CLI_VERSION = "0.1.0-alpha.10";
1
+ export const CLI_VERSION = "0.1.0-alpha.11";
2
2
  //# sourceMappingURL=version.js.map
@@ -3,7 +3,7 @@ import { parseNotificationPreferences, readNotificationDigest, readNotificationP
3
3
  import { assertLoopbackHost, isLoopbackHost, removeRuntimeLock, writeRuntimeLock, } from "../../runtime/src/index.js";
4
4
  const DEFAULT_HOST = "127.0.0.1";
5
5
  const DEFAULT_PORT = 47831;
6
- const DEFAULT_VERSION = "0.1.0-alpha.10";
6
+ const DEFAULT_VERSION = "0.1.0-alpha.11";
7
7
  export async function startLocalApiServer(options = {}) {
8
8
  const host = options.host ?? DEFAULT_HOST;
9
9
  const requestedPort = options.port ?? DEFAULT_PORT;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moneysiren/app",
3
- "version": "0.1.0-alpha.10",
3
+ "version": "0.1.0-alpha.11",
4
4
  "description": "One-command installer for the MoneySiren CLI, local web dashboard, and HUD.",
5
5
  "private": false,
6
6
  "license": "MIT",
@@ -21,6 +21,7 @@
21
21
  "dist/apps/cli/src/**/*.d.ts",
22
22
  "dist/packages/**/*.js",
23
23
  "dist/packages/**/*.d.ts",
24
+ "scripts/preinstall.mjs",
24
25
  "scripts/postinstall.mjs",
25
26
  "README.md",
26
27
  "LICENSE"
@@ -31,10 +32,11 @@
31
32
  "scripts": {
32
33
  "build": "node ../../tools/scripts/build-app-package.mjs",
33
34
  "prepack": "node ../../tools/scripts/build-app-package.mjs",
35
+ "preinstall": "node scripts/preinstall.mjs",
34
36
  "postinstall": "node scripts/postinstall.mjs",
35
37
  "pack:dry-run": "npm pack --dry-run",
36
- "test": "node --check scripts/postinstall.mjs",
37
- "typecheck": "node --check scripts/postinstall.mjs",
38
- "lint": "node --check scripts/postinstall.mjs"
38
+ "test": "node --check scripts/preinstall.mjs && node --check scripts/postinstall.mjs",
39
+ "typecheck": "node --check scripts/preinstall.mjs && node --check scripts/postinstall.mjs",
40
+ "lint": "node --check scripts/preinstall.mjs && node --check scripts/postinstall.mjs"
39
41
  }
40
42
  }
@@ -0,0 +1,87 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { existsSync, lstatSync, readFileSync, readlinkSync, rmSync } from "node:fs";
4
+ import { join, resolve } from "node:path";
5
+
6
+ if (isTruthy(process.env.MONEYSIREN_SKIP_APP_PREINSTALL)) {
7
+ process.exit(0);
8
+ }
9
+
10
+ if (!isGlobalInstall()) {
11
+ process.exit(0);
12
+ }
13
+
14
+ const prefix = process.env.npm_config_prefix;
15
+
16
+ if (!prefix) {
17
+ console.warn("MoneySiren app preinstall skipped: npm global prefix was not provided.");
18
+ process.exit(0);
19
+ }
20
+
21
+ const binDir = process.platform === "win32" ? prefix : join(prefix, "bin");
22
+ const shimNames = process.platform === "win32"
23
+ ? ["moneysiren", "moneysiren.cmd", "moneysiren.ps1", "msiren", "msiren.cmd", "msiren.ps1"]
24
+ : ["moneysiren", "msiren"];
25
+ const removed = [];
26
+
27
+ for (const shimName of shimNames) {
28
+ const shimPath = resolve(binDir, shimName);
29
+
30
+ if (!isInside(resolve(binDir), shimPath) || !existsSync(shimPath)) {
31
+ continue;
32
+ }
33
+
34
+ if (!isMoneySirenShim(shimPath)) {
35
+ continue;
36
+ }
37
+
38
+ try {
39
+ rmSync(shimPath, {
40
+ force: true,
41
+ });
42
+ removed.push(shimName);
43
+ } catch (error) {
44
+ const message = error instanceof Error ? error.message : String(error);
45
+ console.warn(`MoneySiren app preinstall could not remove stale global command ${shimName}: ${message}`);
46
+ }
47
+ }
48
+
49
+ if (removed.length > 0) {
50
+ console.log(`MoneySiren app preinstall removed stale global command shim(s): ${removed.join(", ")}`);
51
+ }
52
+
53
+ function isGlobalInstall() {
54
+ return process.env.npm_config_global === "true" ||
55
+ process.env.npm_config_location === "global" ||
56
+ isTruthy(process.env.MONEYSIREN_APP_GLOBAL_PREINSTALL);
57
+ }
58
+
59
+ function isMoneySirenShim(filePath) {
60
+ try {
61
+ const stat = lstatSync(filePath);
62
+ const source = stat.isSymbolicLink()
63
+ ? readlinkSync(filePath)
64
+ : stat.isFile()
65
+ ? readFileSync(filePath, "utf8")
66
+ : "";
67
+
68
+ return /@moneysiren[\\/]app|@moneysiren[\\/]cli|moneysiren-app|moneysiren-cli/i.test(source);
69
+ } catch {
70
+ return false;
71
+ }
72
+ }
73
+
74
+ function isInside(parent, child) {
75
+ const relative = child.slice(parent.length);
76
+ return child === parent || (child.startsWith(parent) && /^[/\\]/.test(relative));
77
+ }
78
+
79
+ function isTruthy(value) {
80
+ if (value === undefined) {
81
+ return false;
82
+ }
83
+
84
+ const normalized = value.trim().toLowerCase();
85
+
86
+ return normalized.length > 0 && normalized !== "0" && normalized !== "false" && normalized !== "no";
87
+ }