moneyos 0.5.0 → 0.5.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.
package/CHANGELOG.md ADDED
@@ -0,0 +1,158 @@
1
+ # Changelog
2
+
3
+ All notable changes to the repo's current `main` branch are documented here.
4
+
5
+ ## 0.5.1 - 2026-04-13
6
+
7
+ ### Fixed
8
+
9
+ - publish workflow now upgrades npm to latest before publish, which
10
+ activates the Trusted Publishing OIDC exchange that Node 22's bundled
11
+ npm does not support out of the box; without this the previous
12
+ prerelease attempts failed at npm auth despite correct Trusted
13
+ Publisher configuration
14
+ - publish command now includes `--provenance` again; it was accidentally
15
+ dropped during the `0.5.1-rc.1` auth-path revert, which would have
16
+ resulted in unsigned releases even if publish succeeded
17
+
18
+ ## 0.5.1-rc.1 - 2026-04-13
19
+
20
+ ### Changed
21
+
22
+ - root release publishing now runs from a tag-triggered GitHub Actions workflow
23
+ that verifies tag-to-version alignment, reruns lint/typecheck/tests, verifies
24
+ packed tarballs, and publishes with npm Trusted Publishing plus provenance
25
+ - root release package metadata now uses the canonical `1231CheGites/MoneyOS`
26
+ GitHub URL casing for repository links and provenance alignment
27
+ - `CONTRIBUTING.md` now documents the forward release tag convention:
28
+ `moneyos-v<version>` for root releases and `moneyos-<package>-v<version>` for
29
+ workspace packages, with bare `v<version>` retained only for historical tags
30
+ - the publish workflow now isolates the actual `npm publish` command from
31
+ token-based npm auth config and publishes prerelease versions under the
32
+ `next` dist-tag instead of `latest`
33
+
34
+ ## 0.5.0 - 2026-04-12
35
+
36
+ ### Added
37
+
38
+ - `moneyos` now ships first-class CLI tool install/use UX with `moneyos add`,
39
+ `moneyos remove`, and `moneyos tools`
40
+ - root CLI tool loading now uses a user-scoped registry and lazy-loads
41
+ installed tool packages on demand instead of baking tool logic into root
42
+
43
+ ### Fixed
44
+
45
+ - runtime errors thrown by installed tools now pass through to the user instead
46
+ of being mislabeled as broken-tool repair errors; only load/validation
47
+ failures are wrapped as installed-tool breakage
48
+
49
+ ## 0.4.1 - 2026-04-12
50
+
51
+ ### Fixed
52
+
53
+ - fixed the published `moneyos` CLI entrypoint so `npm install moneyos` now
54
+ produces a working `moneyos` binary from `node_modules/.bin`; `0.4.0`
55
+ incorrectly exited without printing help or version because the entrypoint
56
+ guard did not resolve npm's symlinked bin path
57
+
58
+ ## 0.4.0 - 2026-04-12
59
+
60
+ ### Changed
61
+
62
+ - `moneyos` now treats swap as an external package instead of a built-in root
63
+ workflow; install `@moneyos/swap` separately when you want swap support
64
+ - the root README and developer docs now point at the published workspace
65
+ packages and the current package boundary split
66
+
67
+ ## 0.3.4 - 2026-04-12
68
+
69
+ ### Added
70
+
71
+ - added `moneyos auth change-password` so users can rotate the active wallet
72
+ password without changing wallet identity metadata; a successful rotation
73
+ also locks the current local session and leaves existing backup files as
74
+ old-password snapshots until a fresh export is created
75
+
76
+ ### Changed
77
+
78
+ - added CLI-level regression coverage for legacy plaintext wallet migration,
79
+ `moneyos init --force`, malformed `moneyos init --key`, backup export
80
+ messaging, and wallet password rotation flows
81
+
82
+ ### Fixed
83
+
84
+ - `moneyos backup export` now states plainly that exported backups use the same
85
+ wallet password as the active wallet and that restore requires that same
86
+ password
87
+ - insecure backup export destinations now report destination-specific
88
+ permission errors instead of misleading wallet-path errors
89
+
90
+ ## 0.3.3 - 2026-04-11
91
+
92
+ ### Removed
93
+
94
+ - removed the unused Particle executor workspace, smoke script, and supporting
95
+ docs/build references so the repo matches the current local-wallet-first
96
+ product surface
97
+
98
+ ## 0.3.2 - 2026-04-11
99
+
100
+ ### Changed
101
+
102
+ - corrected the README's published-package section so the npm package page no
103
+ longer claims `moneyos` is still on a pre-encrypted-wallet release
104
+
105
+ ## 0.3.1 - 2026-04-11
106
+
107
+ ### Fixed
108
+
109
+ - `moneyos --version` now reports the package version from `package.json`
110
+ instead of a stale hardcoded string
111
+ - added regression coverage to keep the CLI version output aligned with the
112
+ published package version
113
+
114
+ ## 0.3.0 - 2026-04-11
115
+
116
+ ### Added
117
+
118
+ - encrypted local wallet storage at `~/.moneyos/wallet.json`
119
+ - `moneyos auth unlock|lock|status` for local session-based write access
120
+ - encrypted wallet backup support via `moneyos backup export|restore|status`
121
+ - tests for encrypted wallet storage, session lifecycle, prompt TTY gating, and
122
+ backup safety checks
123
+ - GitHub Actions CI workflow running lint, typecheck, tests, and builds across
124
+ the root package and every workspace package on every pull request and on
125
+ pushes to `main`
126
+
127
+ ### Changed
128
+
129
+ - `~/.moneyos/config.json` now stores non-secret settings only
130
+ - legacy plaintext `config.json.privateKey` state is treated as import-only, not
131
+ active runtime wallet state
132
+ - the removed 1Password-backed wallet model remains unsupported
133
+ - the README and wallet architecture docs now describe the encrypted-wallet
134
+ model as the current source of truth
135
+ - `@moneyos/core` is now bundled into the published `moneyos` package; it is
136
+ internal workspace plumbing and consumers no longer need to resolve it
137
+ - `package-lock.json` is now tracked in git for reproducible installs across CI
138
+ and developer machines
139
+
140
+ ### Fixed
141
+
142
+ - session-backed `send` and `swap` no longer use the same `750ms` timeout as
143
+ `auth status` and `auth lock`
144
+ - session-backed send requests now use a longer RPC-appropriate timeout budget
145
+ - the daemon send path has regression coverage for slow local IPC round trips
146
+ - a leaky wallet-path test now uses an isolated tmpdir instead of the
147
+ developer's real `~/.moneyos/wallet.json`
148
+ - the published `moneyos` tarball now installs cleanly as a standalone package;
149
+ earlier builds leaked `@moneyos/core` as a runtime import in both the emitted
150
+ JS and the emitted type declarations, so `npm install moneyos` in a clean
151
+ directory could not resolve its own runtime dependencies
152
+ - `EOAExecutor.send` now awaits the transaction receipt before resolving and
153
+ throws on a non-success receipt; sequenced flows like "approve then swap" now
154
+ work on the first attempt against a fresh wallet, and viem's in-memory nonce
155
+ manager no longer drifts past the chain when a preflight rejects a queued
156
+ transaction
157
+ - session tests now use short socket and token paths so they no longer break in
158
+ long temp directories (the POSIX 108-byte unix socket path limit)
package/dist/cli/index.js CHANGED
@@ -4,7 +4,7 @@
4
4
  import { Command as Command10 } from "commander";
5
5
  import { realpathSync } from "fs";
6
6
  import { resolve as resolve3 } from "path";
7
- import { fileURLToPath as fileURLToPath2 } from "url";
7
+ import { fileURLToPath as fileURLToPath3 } from "url";
8
8
 
9
9
  // src/cli/commands/init.ts
10
10
  import { Command } from "commander";
@@ -2151,7 +2151,7 @@ import { spawn as spawn2 } from "child_process";
2151
2151
  import { createRequire } from "module";
2152
2152
  import { existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
2153
2153
  import { dirname as dirname4, join as join4 } from "path";
2154
- import { pathToFileURL } from "url";
2154
+ import { fileURLToPath, pathToFileURL } from "url";
2155
2155
  import { Command as Command8, CommanderError } from "commander";
2156
2156
 
2157
2157
  // src/cli/tools/runtime.ts
@@ -2186,16 +2186,46 @@ function createMoneyOSCliContext(deps = defaultCreateMoneyOSCliContextDependenci
2186
2186
  }
2187
2187
 
2188
2188
  // src/cli/tools/manager.ts
2189
- var RESERVED_ROOT_COMMANDS = /* @__PURE__ */ new Set(["init", "auth", "backup", "balance", "send", "keystore", "add", "remove", "tools", "help", "__session-daemon"]);
2189
+ var DEFAULT_RESERVED_ROOT_COMMANDS = /* @__PURE__ */ new Set(["init", "auth", "backup", "balance", "send", "keystore", "add", "remove", "tools", "help", "__session-daemon"]);
2190
2190
  var FIRST_PARTY_TOOL_ALIASES = { swap: "@moneyos/swap" };
2191
- var SHARED_TOOL_HOME_DEPENDENCIES = { "@moneyos/core": "^0.1.0", viem: "^2.45.1" };
2192
2191
  var VALID_COMMAND_SEGMENT = /^[A-Za-z0-9][A-Za-z0-9-]*$/;
2193
2192
  var getPaths = () => ({
2194
2193
  rootDir: getToolHomeDir(),
2195
2194
  packageJsonPath: getToolHomePackageJsonPath(),
2196
2195
  registryPath: getToolRegistryPath()
2197
2196
  });
2198
- function ensureToolHome(paths) {
2197
+ function findRootPackageJsonPath() {
2198
+ let currentDir = dirname4(fileURLToPath(import.meta.url));
2199
+ while (true) {
2200
+ const candidate = join4(currentDir, "package.json");
2201
+ if (existsSync5(candidate)) {
2202
+ const packageJson = JSON.parse(readFileSync4(candidate, "utf8"));
2203
+ if (packageJson.name === "moneyos") return candidate;
2204
+ }
2205
+ const parentDir = dirname4(currentDir);
2206
+ if (parentDir === currentDir) {
2207
+ throw new Error("Could not find the root moneyos package.json.");
2208
+ }
2209
+ currentDir = parentDir;
2210
+ }
2211
+ }
2212
+ function getSharedToolHomeDependencies() {
2213
+ const rootPackageJsonPath = findRootPackageJsonPath();
2214
+ const packageJson = JSON.parse(readFileSync4(rootPackageJsonPath, "utf8"));
2215
+ const coreVersion = packageJson.moneyos?.toolHomeDependencies?.["@moneyos/core"];
2216
+ const viemVersion = packageJson.dependencies?.viem;
2217
+ if (typeof coreVersion !== "string" || typeof viemVersion !== "string") {
2218
+ throw new Error(`Root package metadata at ${rootPackageJsonPath} is missing tool-home dependency versions.`);
2219
+ }
2220
+ return { "@moneyos/core": coreVersion, viem: viemVersion };
2221
+ }
2222
+ function collectReservedRootCommandNames(program) {
2223
+ return /* @__PURE__ */ new Set([
2224
+ "help",
2225
+ ...program.commands.flatMap((command) => [command.name(), ...command.aliases()])
2226
+ ]);
2227
+ }
2228
+ function ensureToolHome(paths, sharedToolHomeDependencies) {
2199
2229
  if (!existsSync5(paths.rootDir)) mkdirSync4(paths.rootDir, { recursive: true, mode: 448 });
2200
2230
  const exists = existsSync5(paths.packageJsonPath);
2201
2231
  const parsed = exists ? JSON.parse(readFileSync4(paths.packageJsonPath, "utf8")) : void 0;
@@ -2206,7 +2236,7 @@ function ensureToolHome(paths) {
2206
2236
  dependencies: { ...parsed?.dependencies ?? {} }
2207
2237
  };
2208
2238
  let changed = !exists;
2209
- for (const [name, version2] of Object.entries(SHARED_TOOL_HOME_DEPENDENCIES)) {
2239
+ for (const [name, version2] of Object.entries(sharedToolHomeDependencies)) {
2210
2240
  if (packageJson.dependencies[name] !== version2) {
2211
2241
  packageJson.dependencies[name] = version2;
2212
2242
  changed = true;
@@ -2228,20 +2258,9 @@ function parseLoadedTool(packageName, packageVersion, value) {
2228
2258
  if (tool.version !== 1 || typeof tool.name !== "string" || typeof tool.description !== "string" || !Array.isArray(tool.commandPath) || tool.commandPath.length === 0 || tool.commandPath.some((segment) => typeof segment !== "string" || !VALID_COMMAND_SEGMENT.test(segment)) || typeof tool.createCommand !== "function") throw new Error(`Package ${packageName} exports an invalid \`moneyosCliTool\`.`);
2229
2259
  return { packageName, packageVersion, toolVersion: 1, name: tool.name, commandPath: [...tool.commandPath], description: tool.description, createCommand: tool.createCommand };
2230
2260
  }
2231
- function readRegistry(paths) {
2232
- ensureToolHome(paths);
2233
- const parsed = JSON.parse(readFileSync4(paths.registryPath, "utf8"));
2234
- if (!Array.isArray(parsed)) throw new Error(`Tool registry at ${paths.registryPath} is invalid.`);
2235
- return parsed.map(parseRegistryEntry);
2236
- }
2237
- function writeRegistry(paths, entries) {
2238
- ensureToolHome(paths);
2239
- writeFileSync3(paths.registryPath, `${JSON.stringify(entries, null, 2)}
2240
- `, { mode: 384 });
2241
- }
2242
- function getConflict(entry, entries) {
2261
+ function getConflict(entry, entries, reservedRootCommands) {
2243
2262
  const path = entry.commandPath.join(" ");
2244
- if (RESERVED_ROOT_COMMANDS.has(entry.name) || RESERVED_ROOT_COMMANDS.has(entry.commandPath[0])) {
2263
+ if (reservedRootCommands.has(entry.name) || reservedRootCommands.has(entry.commandPath[0])) {
2245
2264
  return `reserved root command collision for ${path}`;
2246
2265
  }
2247
2266
  for (const other of entries) {
@@ -2289,6 +2308,9 @@ async function loadInstalledToolFromFs(paths, packageName) {
2289
2308
  function createCliToolManager(params = {}) {
2290
2309
  const paths = params.paths ?? getPaths();
2291
2310
  const cliContext = params.cliContext ?? createMoneyOSCliContext();
2311
+ const sharedToolHomeDependencies = params.sharedToolHomeDependencies ?? getSharedToolHomeDependencies();
2312
+ const sharedToolHomeDependencyNames = new Set(Object.keys(sharedToolHomeDependencies));
2313
+ let reservedRootCommands = new Set(params.reservedRootCommands ?? DEFAULT_RESERVED_ROOT_COMMANDS);
2292
2314
  const install = params.packageManager?.install ?? ((toolPaths, spec) => runNpm(toolPaths, ["install", "--save-exact", "--no-fund", "--no-audit", spec]));
2293
2315
  const uninstall = params.packageManager?.uninstall ?? ((toolPaths, packageName) => runNpm(toolPaths, ["uninstall", "--no-fund", "--no-audit", packageName]));
2294
2316
  const loadInstalledTool = params.moduleLoader ? async (toolPaths, packageName) => {
@@ -2300,6 +2322,17 @@ function createCliToolManager(params = {}) {
2300
2322
  const matches = entries.filter((entry) => entry.name === input || entry.commandPath.join(" ") === input);
2301
2323
  return matches.length === 1 ? matches[0] : void 0;
2302
2324
  })();
2325
+ const getRegistryEntries = () => {
2326
+ ensureToolHome(paths, sharedToolHomeDependencies);
2327
+ const parsed = JSON.parse(readFileSync4(paths.registryPath, "utf8"));
2328
+ if (!Array.isArray(parsed)) throw new Error(`Tool registry at ${paths.registryPath} is invalid.`);
2329
+ return parsed.map(parseRegistryEntry);
2330
+ };
2331
+ const writeRegistryEntries = (entries) => {
2332
+ ensureToolHome(paths, sharedToolHomeDependencies);
2333
+ writeFileSync3(paths.registryPath, `${JSON.stringify(entries, null, 2)}
2334
+ `, { mode: 384 });
2335
+ };
2303
2336
  async function invoke(entry, args) {
2304
2337
  let command;
2305
2338
  try {
@@ -2326,18 +2359,19 @@ function createCliToolManager(params = {}) {
2326
2359
  }
2327
2360
  return {
2328
2361
  getRegistryEntries() {
2329
- return readRegistry(paths);
2362
+ return getRegistryEntries();
2330
2363
  },
2331
2364
  mountInstalledToolCommands(program) {
2332
2365
  let entries;
2333
2366
  try {
2334
- entries = readRegistry(paths);
2367
+ entries = getRegistryEntries();
2335
2368
  } catch {
2336
2369
  return;
2337
2370
  }
2371
+ reservedRootCommands = collectReservedRootCommandNames(program);
2338
2372
  const groups = /* @__PURE__ */ new Map();
2339
2373
  for (const entry of entries) {
2340
- if (getConflict(entry, entries)) continue;
2374
+ if (getConflict(entry, entries, reservedRootCommands)) continue;
2341
2375
  let parent = program;
2342
2376
  const segments = [];
2343
2377
  for (const segment of entry.commandPath.slice(0, -1)) {
@@ -2359,20 +2393,20 @@ function createCliToolManager(params = {}) {
2359
2393
  },
2360
2394
  async addTool(input) {
2361
2395
  const spec = FIRST_PARTY_TOOL_ALIASES[input] ?? input;
2362
- const entries = readRegistry(paths);
2396
+ const entries = getRegistryEntries();
2363
2397
  const previous = resolveInstalledTool(input, entries);
2364
- ensureToolHome(paths);
2398
+ ensureToolHome(paths, sharedToolHomeDependencies);
2365
2399
  const before = JSON.parse(readFileSync4(paths.packageJsonPath, "utf8")).dependencies ?? {};
2366
2400
  await install(paths, spec);
2367
2401
  const after = JSON.parse(readFileSync4(paths.packageJsonPath, "utf8")).dependencies ?? {};
2368
- const changed = previous?.packageName ?? Object.keys(after).find((name) => before[name] !== after[name] && !(name in SHARED_TOOL_HOME_DEPENDENCIES));
2402
+ const changed = previous?.packageName ?? Object.keys(after).find((name) => before[name] !== after[name] && !sharedToolHomeDependencyNames.has(name));
2369
2403
  try {
2370
2404
  const loaded = await loadInstalledTool(paths, changed ?? spec);
2371
2405
  const next = previous ? entries.filter((entry2) => entry2.packageName !== previous.packageName) : entries;
2372
- const conflict = getConflict(loaded, next);
2406
+ const conflict = getConflict(loaded, next, reservedRootCommands);
2373
2407
  if (conflict) throw new Error(conflict);
2374
2408
  const entry = { packageName: loaded.packageName, packageVersion: loaded.packageVersion, toolVersion: loaded.toolVersion, name: loaded.name, commandPath: loaded.commandPath, description: loaded.description };
2375
- writeRegistry(paths, [...next, entry]);
2409
+ writeRegistryEntries([...next, entry]);
2376
2410
  return entry;
2377
2411
  } catch (error) {
2378
2412
  try {
@@ -2384,17 +2418,17 @@ function createCliToolManager(params = {}) {
2384
2418
  }
2385
2419
  },
2386
2420
  async removeTool(input) {
2387
- const entries = readRegistry(paths);
2421
+ const entries = getRegistryEntries();
2388
2422
  const entry = resolveInstalledTool(input, entries);
2389
2423
  if (!entry) throw new Error(`Tool ${input} is not installed.`);
2390
2424
  await uninstall(paths, entry.packageName);
2391
- writeRegistry(paths, entries.filter((installed) => installed.packageName !== entry.packageName));
2425
+ writeRegistryEntries(entries.filter((installed) => installed.packageName !== entry.packageName));
2392
2426
  return entry;
2393
2427
  },
2394
2428
  async listTools() {
2395
- const entries = readRegistry(paths);
2429
+ const entries = getRegistryEntries();
2396
2430
  const tools = await Promise.all(entries.map(async (entry) => {
2397
- const conflict = getConflict(entry, entries);
2431
+ const conflict = getConflict(entry, entries, reservedRootCommands);
2398
2432
  if (conflict) return { ...entry, state: "conflict", problems: [conflict] };
2399
2433
  try {
2400
2434
  const loaded = await loadInstalledTool(paths, entry.packageName);
@@ -2464,10 +2498,10 @@ function createToolsCommand(toolManager) {
2464
2498
 
2465
2499
  // src/cli/version.ts
2466
2500
  import { readFileSync as readFileSync5 } from "fs";
2467
- import { fileURLToPath } from "url";
2501
+ import { fileURLToPath as fileURLToPath2 } from "url";
2468
2502
  import { dirname as dirname5, resolve as resolve2 } from "path";
2469
2503
  var packageJsonPath = resolve2(
2470
- dirname5(fileURLToPath(import.meta.url)),
2504
+ dirname5(fileURLToPath2(import.meta.url)),
2471
2505
  "../../package.json"
2472
2506
  );
2473
2507
  var version = JSON.parse(
@@ -2488,10 +2522,10 @@ function createProgram(options = {}) {
2488
2522
  program.addCommand(createAddToolCommand(toolManager));
2489
2523
  program.addCommand(createRemoveToolCommand(toolManager));
2490
2524
  program.addCommand(createToolsCommand(toolManager));
2491
- toolManager.mountInstalledToolCommands(program);
2492
2525
  program.command("__session-daemon", { hidden: true }).action(async () => {
2493
2526
  await runSessionDaemonProcess();
2494
2527
  });
2528
+ toolManager.mountInstalledToolCommands(program);
2495
2529
  return program;
2496
2530
  }
2497
2531
  function resolveEntrypointPath(path) {
@@ -2502,7 +2536,7 @@ function resolveEntrypointPath(path) {
2502
2536
  }
2503
2537
  }
2504
2538
  function isEntrypointPath(cliEntry, moduleUrl) {
2505
- return typeof cliEntry === "string" && resolveEntrypointPath(cliEntry) === fileURLToPath2(moduleUrl);
2539
+ return typeof cliEntry === "string" && resolveEntrypointPath(cliEntry) === fileURLToPath3(moduleUrl);
2506
2540
  }
2507
2541
  if (isEntrypointPath(process.argv[1], import.meta.url)) {
2508
2542
  void createProgram().parseAsync(process.argv).catch((error) => {