moneyos 0.4.0 → 0.5.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 +48 -7
- package/dist/cli/index.js +413 -25
- package/dist/cli/index.js.map +1 -1
- package/dist/index.cjs +221 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +23 -1
- package/dist/index.d.ts +23 -1
- package/dist/index.js +218 -9
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -26,6 +26,10 @@ moneyos auth unlock
|
|
|
26
26
|
moneyos auth lock
|
|
27
27
|
moneyos auth status
|
|
28
28
|
moneyos auth change-password
|
|
29
|
+
moneyos add <tool>
|
|
30
|
+
moneyos remove <tool>
|
|
31
|
+
moneyos tools
|
|
32
|
+
moneyos swap <amount> <tokenIn> <tokenOut> [--chain <id>] [--provider odos]
|
|
29
33
|
moneyos backup export [--out ./wallet-backup.json] [--force]
|
|
30
34
|
moneyos backup restore <path> [--force]
|
|
31
35
|
moneyos backup status
|
|
@@ -41,8 +45,9 @@ Example:
|
|
|
41
45
|
```bash
|
|
42
46
|
moneyos init
|
|
43
47
|
moneyos auth unlock
|
|
44
|
-
moneyos
|
|
45
|
-
moneyos
|
|
48
|
+
moneyos add swap
|
|
49
|
+
moneyos tools
|
|
50
|
+
moneyos swap 0.1 RYZE ETH
|
|
46
51
|
```
|
|
47
52
|
|
|
48
53
|
## SDK
|
|
@@ -62,14 +67,48 @@ const tx = await moneyos.send("USDC", "0x...", "10");
|
|
|
62
67
|
console.log(tx.hash);
|
|
63
68
|
```
|
|
64
69
|
|
|
70
|
+
If you want a local script or workflow to reuse an already-unlocked MoneyOS
|
|
71
|
+
session, unlock first in the terminal:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
moneyos auth unlock
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Then attach that session explicitly in code:
|
|
78
|
+
|
|
79
|
+
```ts
|
|
80
|
+
import { createMoneyOS, connectLocalSession } from "moneyos";
|
|
81
|
+
|
|
82
|
+
const execute = await connectLocalSession();
|
|
83
|
+
const moneyos = createMoneyOS({
|
|
84
|
+
chainId: 42161,
|
|
85
|
+
execute,
|
|
86
|
+
});
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Tool packages should still execute against `moneyos.runtime`; they should not
|
|
90
|
+
import `connectLocalSession()` themselves.
|
|
91
|
+
|
|
65
92
|
The runtime seam is intentionally small. `createMoneyOS` can also take injected
|
|
66
93
|
`execute`, `read`, and `assets` implementations, which is how external packages
|
|
67
94
|
plug in.
|
|
68
95
|
|
|
69
|
-
Swap
|
|
70
|
-
|
|
96
|
+
Swap still lives in the separate `@moneyos/swap` package, but the root CLI now
|
|
97
|
+
owns first-class tool install/use UX for CLI-integrated packages.
|
|
98
|
+
|
|
99
|
+
For CLI usage, install the root package and then add tools into the user-level
|
|
100
|
+
MoneyOS tool home:
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
npm install moneyos
|
|
104
|
+
moneyos init
|
|
105
|
+
moneyos auth unlock
|
|
106
|
+
moneyos add swap
|
|
107
|
+
moneyos tools
|
|
108
|
+
moneyos swap 0.1 RYZE ETH
|
|
109
|
+
```
|
|
71
110
|
|
|
72
|
-
|
|
111
|
+
For direct package usage, install the tool package alongside `moneyos`:
|
|
73
112
|
|
|
74
113
|
```bash
|
|
75
114
|
npm install moneyos @moneyos/swap
|
|
@@ -108,8 +147,9 @@ Published packages:
|
|
|
108
147
|
- `@moneyos/core`
|
|
109
148
|
- `@moneyos/swap`
|
|
110
149
|
|
|
111
|
-
`moneyos
|
|
112
|
-
swap, install
|
|
150
|
+
Current `moneyos` releases no longer bundle swap into the root SDK or CLI. If
|
|
151
|
+
you want swap from the root CLI, install `moneyos` and then run
|
|
152
|
+
`moneyos add swap`.
|
|
113
153
|
|
|
114
154
|
## Current wallet model
|
|
115
155
|
|
|
@@ -119,6 +159,7 @@ What is landed in code today:
|
|
|
119
159
|
- `~/.moneyos/config.json` now stores only non-secret settings such as chain and RPC configuration
|
|
120
160
|
- `MONEYOS_PRIVATE_KEY` remains an explicit override for ephemeral CI or agent runs
|
|
121
161
|
- `moneyos auth unlock` opens a short-lived local session for write commands
|
|
162
|
+
- workflow scripts can attach to that unlocked session with `connectLocalSession()`
|
|
122
163
|
- `moneyos auth change-password` rotates the local wallet password and locks the current session
|
|
123
164
|
- `moneyos backup export|restore|status` manages encrypted wallet backups
|
|
124
165
|
- Normal wallet commands resolve their write path through one shared session-aware flow
|
package/dist/cli/index.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/cli/index.ts
|
|
4
|
-
import { Command as
|
|
4
|
+
import { Command as Command10 } from "commander";
|
|
5
|
+
import { realpathSync } from "fs";
|
|
5
6
|
import { resolve as resolve3 } from "path";
|
|
6
7
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
7
8
|
|
|
@@ -510,6 +511,15 @@ function saveConfig(config, path = CONFIG_FILE) {
|
|
|
510
511
|
function getConfigPath() {
|
|
511
512
|
return CONFIG_FILE;
|
|
512
513
|
}
|
|
514
|
+
function getToolHomeDir() {
|
|
515
|
+
return join3(CONFIG_DIR, "tools");
|
|
516
|
+
}
|
|
517
|
+
function getToolHomePackageJsonPath() {
|
|
518
|
+
return join3(getToolHomeDir(), "package.json");
|
|
519
|
+
}
|
|
520
|
+
function getToolRegistryPath() {
|
|
521
|
+
return join3(getToolHomeDir(), "registry.json");
|
|
522
|
+
}
|
|
513
523
|
function getWalletPath(config) {
|
|
514
524
|
return config?.walletPath ?? join3(CONFIG_DIR, "wallet.json");
|
|
515
525
|
}
|
|
@@ -1441,6 +1451,43 @@ import {
|
|
|
1441
1451
|
parseUnits,
|
|
1442
1452
|
encodeFunctionData
|
|
1443
1453
|
} from "viem";
|
|
1454
|
+
|
|
1455
|
+
// src/core/assets.ts
|
|
1456
|
+
var DefaultAssetRegistry = class {
|
|
1457
|
+
nativeTokenAddress = NATIVE_TOKEN_ADDRESS;
|
|
1458
|
+
getToken = getToken;
|
|
1459
|
+
getTokenAddress = getTokenAddress;
|
|
1460
|
+
getChain = getChain;
|
|
1461
|
+
};
|
|
1462
|
+
|
|
1463
|
+
// src/core/no-executor.ts
|
|
1464
|
+
var NO_SIGNING_ACCOUNT_ERROR = "No signing account configured. Set `signer`, `privateKey`, or `execute` in MoneyOS config.";
|
|
1465
|
+
function throwNoSigningAccount() {
|
|
1466
|
+
throw new Error(NO_SIGNING_ACCOUNT_ERROR);
|
|
1467
|
+
}
|
|
1468
|
+
function createNoExecutorClient() {
|
|
1469
|
+
return {
|
|
1470
|
+
mode: "eoa",
|
|
1471
|
+
getAddress() {
|
|
1472
|
+
return throwNoSigningAccount();
|
|
1473
|
+
},
|
|
1474
|
+
async send(_call) {
|
|
1475
|
+
return throwNoSigningAccount();
|
|
1476
|
+
},
|
|
1477
|
+
async sendBatch(_calls) {
|
|
1478
|
+
return throwNoSigningAccount();
|
|
1479
|
+
},
|
|
1480
|
+
capabilities() {
|
|
1481
|
+
return {
|
|
1482
|
+
sponsoredGas: false,
|
|
1483
|
+
batching: false,
|
|
1484
|
+
simulation: false
|
|
1485
|
+
};
|
|
1486
|
+
}
|
|
1487
|
+
};
|
|
1488
|
+
}
|
|
1489
|
+
|
|
1490
|
+
// src/core/client.ts
|
|
1444
1491
|
var ERC20_ABI = [
|
|
1445
1492
|
{
|
|
1446
1493
|
name: "balanceOf",
|
|
@@ -1474,12 +1521,6 @@ var ERC20_ABI = [
|
|
|
1474
1521
|
outputs: [{ name: "", type: "string" }]
|
|
1475
1522
|
}
|
|
1476
1523
|
];
|
|
1477
|
-
var DefaultAssetRegistry = class {
|
|
1478
|
-
nativeTokenAddress = NATIVE_TOKEN_ADDRESS;
|
|
1479
|
-
getToken = getToken;
|
|
1480
|
-
getTokenAddress = getTokenAddress;
|
|
1481
|
-
getChain = getChain;
|
|
1482
|
-
};
|
|
1483
1524
|
var MoneyOS = class {
|
|
1484
1525
|
read;
|
|
1485
1526
|
executor;
|
|
@@ -1525,9 +1566,7 @@ var MoneyOS = class {
|
|
|
1525
1566
|
}
|
|
1526
1567
|
requireExecutor() {
|
|
1527
1568
|
if (!this.executor) {
|
|
1528
|
-
throw new Error(
|
|
1529
|
-
"No signing account configured. Set `signer`, `privateKey`, or `execute` in MoneyOS config."
|
|
1530
|
-
);
|
|
1569
|
+
throw new Error(NO_SIGNING_ACCOUNT_ERROR);
|
|
1531
1570
|
}
|
|
1532
1571
|
return this.executor;
|
|
1533
1572
|
}
|
|
@@ -1604,6 +1643,23 @@ var MoneyOS = class {
|
|
|
1604
1643
|
}
|
|
1605
1644
|
};
|
|
1606
1645
|
|
|
1646
|
+
// src/local-session.ts
|
|
1647
|
+
async function connectLocalSession(options = {}) {
|
|
1648
|
+
const socketPath = options.socketPath ?? getSessionSocketPath();
|
|
1649
|
+
const tokenPath = options.tokenPath ?? getSessionTokenPath();
|
|
1650
|
+
const session = await getSessionStatus(socketPath, tokenPath);
|
|
1651
|
+
if (!session) {
|
|
1652
|
+
throw new Error(
|
|
1653
|
+
"No active local MoneyOS session found. Run `moneyos auth unlock` locally first."
|
|
1654
|
+
);
|
|
1655
|
+
}
|
|
1656
|
+
return new SessionExecutionClient({
|
|
1657
|
+
socketPath,
|
|
1658
|
+
tokenPath,
|
|
1659
|
+
address: session.address
|
|
1660
|
+
});
|
|
1661
|
+
}
|
|
1662
|
+
|
|
1607
1663
|
// src/cli/wallet.ts
|
|
1608
1664
|
function resolveEnvPrivateKey(explicit) {
|
|
1609
1665
|
return explicit ?? process.env.MONEYOS_PRIVATE_KEY;
|
|
@@ -1664,16 +1720,15 @@ async function buildCliMoneyOSConfig(config, options = {}) {
|
|
|
1664
1720
|
}
|
|
1665
1721
|
const socketPath = getSessionPath(options);
|
|
1666
1722
|
const tokenPath = getTokenPath(options);
|
|
1667
|
-
|
|
1668
|
-
if (session) {
|
|
1723
|
+
try {
|
|
1669
1724
|
return {
|
|
1670
1725
|
...moneyosConfig,
|
|
1671
|
-
execute:
|
|
1726
|
+
execute: await connectLocalSession({
|
|
1672
1727
|
socketPath,
|
|
1673
|
-
tokenPath
|
|
1674
|
-
address: session.address
|
|
1728
|
+
tokenPath
|
|
1675
1729
|
})
|
|
1676
1730
|
};
|
|
1731
|
+
} catch {
|
|
1677
1732
|
}
|
|
1678
1733
|
const wallet = getWalletStore(config, options);
|
|
1679
1734
|
if (wallet.exists()) {
|
|
@@ -2088,21 +2143,341 @@ backupCommand.command("status").description("Show the local wallet backup status
|
|
|
2088
2143
|
console.log(formatBackupStatus(status));
|
|
2089
2144
|
});
|
|
2090
2145
|
|
|
2146
|
+
// src/cli/commands/tools.ts
|
|
2147
|
+
import { Command as Command9 } from "commander";
|
|
2148
|
+
|
|
2149
|
+
// src/cli/tools/manager.ts
|
|
2150
|
+
import { spawn as spawn2 } from "child_process";
|
|
2151
|
+
import { createRequire } from "module";
|
|
2152
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
|
|
2153
|
+
import { dirname as dirname4, join as join4 } from "path";
|
|
2154
|
+
import { pathToFileURL } from "url";
|
|
2155
|
+
import { Command as Command8, CommanderError } from "commander";
|
|
2156
|
+
|
|
2157
|
+
// src/cli/tools/runtime.ts
|
|
2158
|
+
import { Command as Command7 } from "commander";
|
|
2159
|
+
var defaultCreateMoneyOSCliContextDependencies = {
|
|
2160
|
+
loadConfig,
|
|
2161
|
+
connectLocalSession,
|
|
2162
|
+
createReadClient: ({ chainId, rpcUrl }) => new ViemReadClient({
|
|
2163
|
+
defaultChainId: chainId,
|
|
2164
|
+
rpcUrl
|
|
2165
|
+
}),
|
|
2166
|
+
createAssets: () => new DefaultAssetRegistry()
|
|
2167
|
+
};
|
|
2168
|
+
function createMoneyOSCliContext(deps = defaultCreateMoneyOSCliContextDependencies) {
|
|
2169
|
+
return {
|
|
2170
|
+
Command: Command7,
|
|
2171
|
+
async getRuntime(options = {}) {
|
|
2172
|
+
const config = deps.loadConfig();
|
|
2173
|
+
const chainId = options.chainId ?? config.chainId ?? 42161;
|
|
2174
|
+
const rpcUrl = config.rpcUrl;
|
|
2175
|
+
return {
|
|
2176
|
+
read: deps.createReadClient({ chainId, rpcUrl }),
|
|
2177
|
+
execute: options.requireSession ? await deps.connectLocalSession() : createNoExecutorClient(),
|
|
2178
|
+
assets: deps.createAssets(),
|
|
2179
|
+
config: {
|
|
2180
|
+
defaultChainId: chainId,
|
|
2181
|
+
rpcUrl
|
|
2182
|
+
}
|
|
2183
|
+
};
|
|
2184
|
+
}
|
|
2185
|
+
};
|
|
2186
|
+
}
|
|
2187
|
+
|
|
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"]);
|
|
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
|
+
var VALID_COMMAND_SEGMENT = /^[A-Za-z0-9][A-Za-z0-9-]*$/;
|
|
2193
|
+
var getPaths = () => ({
|
|
2194
|
+
rootDir: getToolHomeDir(),
|
|
2195
|
+
packageJsonPath: getToolHomePackageJsonPath(),
|
|
2196
|
+
registryPath: getToolRegistryPath()
|
|
2197
|
+
});
|
|
2198
|
+
function ensureToolHome(paths) {
|
|
2199
|
+
if (!existsSync5(paths.rootDir)) mkdirSync4(paths.rootDir, { recursive: true, mode: 448 });
|
|
2200
|
+
const exists = existsSync5(paths.packageJsonPath);
|
|
2201
|
+
const parsed = exists ? JSON.parse(readFileSync4(paths.packageJsonPath, "utf8")) : void 0;
|
|
2202
|
+
const packageJson = {
|
|
2203
|
+
name: parsed?.name ?? "moneyos-tools",
|
|
2204
|
+
private: parsed?.private ?? true,
|
|
2205
|
+
description: parsed?.description ?? "User-level installed MoneyOS CLI tools",
|
|
2206
|
+
dependencies: { ...parsed?.dependencies ?? {} }
|
|
2207
|
+
};
|
|
2208
|
+
let changed = !exists;
|
|
2209
|
+
for (const [name, version2] of Object.entries(SHARED_TOOL_HOME_DEPENDENCIES)) {
|
|
2210
|
+
if (packageJson.dependencies[name] !== version2) {
|
|
2211
|
+
packageJson.dependencies[name] = version2;
|
|
2212
|
+
changed = true;
|
|
2213
|
+
}
|
|
2214
|
+
}
|
|
2215
|
+
if (changed) writeFileSync3(paths.packageJsonPath, `${JSON.stringify(packageJson, null, 2)}
|
|
2216
|
+
`, { mode: 384 });
|
|
2217
|
+
if (!existsSync5(paths.registryPath)) writeFileSync3(paths.registryPath, "[]\n", { mode: 384 });
|
|
2218
|
+
}
|
|
2219
|
+
function parseRegistryEntry(value) {
|
|
2220
|
+
if (typeof value !== "object" || value === null) throw new Error("Invalid tool registry entry.");
|
|
2221
|
+
const entry = value;
|
|
2222
|
+
if (typeof entry.packageName !== "string" || typeof entry.packageVersion !== "string" || entry.toolVersion !== 1 || typeof entry.name !== "string" || typeof entry.description !== "string" || !Array.isArray(entry.commandPath) || entry.commandPath.length === 0 || entry.commandPath.some((segment) => typeof segment !== "string" || !VALID_COMMAND_SEGMENT.test(segment))) throw new Error("Invalid tool registry entry.");
|
|
2223
|
+
return { packageName: entry.packageName, packageVersion: entry.packageVersion, toolVersion: 1, name: entry.name, commandPath: [...entry.commandPath], description: entry.description };
|
|
2224
|
+
}
|
|
2225
|
+
function parseLoadedTool(packageName, packageVersion, value) {
|
|
2226
|
+
if (typeof value !== "object" || value === null) throw new Error(`Package ${packageName} does not export \`moneyosCliTool\`.`);
|
|
2227
|
+
const tool = value;
|
|
2228
|
+
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
|
+
return { packageName, packageVersion, toolVersion: 1, name: tool.name, commandPath: [...tool.commandPath], description: tool.description, createCommand: tool.createCommand };
|
|
2230
|
+
}
|
|
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) {
|
|
2243
|
+
const path = entry.commandPath.join(" ");
|
|
2244
|
+
if (RESERVED_ROOT_COMMANDS.has(entry.name) || RESERVED_ROOT_COMMANDS.has(entry.commandPath[0])) {
|
|
2245
|
+
return `reserved root command collision for ${path}`;
|
|
2246
|
+
}
|
|
2247
|
+
for (const other of entries) {
|
|
2248
|
+
if (other.packageName === entry.packageName) continue;
|
|
2249
|
+
const otherPath = other.commandPath.join(" ");
|
|
2250
|
+
if (path === otherPath || path.startsWith(`${otherPath} `) || otherPath.startsWith(`${path} `)) {
|
|
2251
|
+
return `command path ${path} collides with installed tool path ${otherPath}`;
|
|
2252
|
+
}
|
|
2253
|
+
}
|
|
2254
|
+
}
|
|
2255
|
+
async function runNpm(paths, args) {
|
|
2256
|
+
await new Promise((resolve4, reject) => {
|
|
2257
|
+
const child = spawn2("npm", args, { cwd: paths.rootDir, stdio: "pipe" });
|
|
2258
|
+
let stderr = "";
|
|
2259
|
+
child.stderr.on("data", (chunk) => {
|
|
2260
|
+
stderr += chunk.toString();
|
|
2261
|
+
});
|
|
2262
|
+
child.on("error", reject);
|
|
2263
|
+
child.on("close", (code) => code === 0 ? resolve4() : reject(new Error(stderr.trim() || `npm ${args[0]} failed with exit code ${String(code)}.`)));
|
|
2264
|
+
});
|
|
2265
|
+
}
|
|
2266
|
+
async function loadInstalledToolFromFs(paths, packageName) {
|
|
2267
|
+
const toolRequire = createRequire(paths.packageJsonPath);
|
|
2268
|
+
let entryPath;
|
|
2269
|
+
try {
|
|
2270
|
+
entryPath = toolRequire.resolve(packageName);
|
|
2271
|
+
} catch {
|
|
2272
|
+
throw new Error(`Package ${packageName} is missing from ${join4(paths.rootDir, "node_modules")}.`);
|
|
2273
|
+
}
|
|
2274
|
+
for (let root = dirname4(entryPath); root !== dirname4(root); root = dirname4(root)) {
|
|
2275
|
+
const packageJsonPath2 = join4(root, "package.json");
|
|
2276
|
+
if (!existsSync5(packageJsonPath2)) continue;
|
|
2277
|
+
const packageJson = JSON.parse(readFileSync4(packageJsonPath2, "utf8"));
|
|
2278
|
+
if (packageJson.name === packageName && typeof packageJson.version === "string") {
|
|
2279
|
+
const moduleNamespace = await import(pathToFileURL(entryPath).href);
|
|
2280
|
+
return parseLoadedTool(
|
|
2281
|
+
packageJson.name,
|
|
2282
|
+
packageJson.version,
|
|
2283
|
+
moduleNamespace.moneyosCliTool ?? moduleNamespace.default?.moneyosCliTool
|
|
2284
|
+
);
|
|
2285
|
+
}
|
|
2286
|
+
}
|
|
2287
|
+
throw new Error(`Could not find package.json for installed package ${packageName}.`);
|
|
2288
|
+
}
|
|
2289
|
+
function createCliToolManager(params = {}) {
|
|
2290
|
+
const paths = params.paths ?? getPaths();
|
|
2291
|
+
const cliContext = params.cliContext ?? createMoneyOSCliContext();
|
|
2292
|
+
const install = params.packageManager?.install ?? ((toolPaths, spec) => runNpm(toolPaths, ["install", "--save-exact", "--no-fund", "--no-audit", spec]));
|
|
2293
|
+
const uninstall = params.packageManager?.uninstall ?? ((toolPaths, packageName) => runNpm(toolPaths, ["uninstall", "--no-fund", "--no-audit", packageName]));
|
|
2294
|
+
const loadInstalledTool = params.moduleLoader ? async (toolPaths, packageName) => {
|
|
2295
|
+
const loaded = await params.moduleLoader(toolPaths, packageName);
|
|
2296
|
+
return parseLoadedTool(loaded.packageName, loaded.packageVersion, loaded.cliTool);
|
|
2297
|
+
} : loadInstalledToolFromFs;
|
|
2298
|
+
const sameMetadata = (left, right) => left.packageName === right.packageName && left.packageVersion === right.packageVersion && left.toolVersion === right.toolVersion && left.name === right.name && left.description === right.description && left.commandPath.join("\0") === right.commandPath.join("\0");
|
|
2299
|
+
const resolveInstalledTool = (input, entries) => entries.find((entry) => entry.packageName === input) ?? entries.find((entry) => entry.packageName === FIRST_PARTY_TOOL_ALIASES[input]) ?? (() => {
|
|
2300
|
+
const matches = entries.filter((entry) => entry.name === input || entry.commandPath.join(" ") === input);
|
|
2301
|
+
return matches.length === 1 ? matches[0] : void 0;
|
|
2302
|
+
})();
|
|
2303
|
+
async function invoke(entry, args) {
|
|
2304
|
+
let command;
|
|
2305
|
+
try {
|
|
2306
|
+
const loaded = await loadInstalledTool(paths, entry.packageName);
|
|
2307
|
+
if (!sameMetadata(entry, loaded)) throw new Error("installed metadata does not match registry");
|
|
2308
|
+
command = loaded.createCommand(cliContext);
|
|
2309
|
+
if (!(command instanceof Command8) || command.name() !== entry.commandPath[entry.commandPath.length - 1]) {
|
|
2310
|
+
throw new Error("createCommand() did not return the expected command");
|
|
2311
|
+
}
|
|
2312
|
+
command.exitOverride();
|
|
2313
|
+
} catch (error) {
|
|
2314
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2315
|
+
throw new Error(`Installed tool ${entry.commandPath.join(" ")} is broken: ${message}. Run \`moneyos add ${entry.packageName}\` to repair it or \`moneyos remove ${entry.packageName}\` to uninstall it.`);
|
|
2316
|
+
}
|
|
2317
|
+
try {
|
|
2318
|
+
await command.parseAsync([process.execPath, entry.commandPath[entry.commandPath.length - 1], ...args]);
|
|
2319
|
+
} catch (error) {
|
|
2320
|
+
if (error instanceof CommanderError) {
|
|
2321
|
+
if (error.code !== "commander.helpDisplayed") process.exitCode = error.exitCode || 1;
|
|
2322
|
+
return;
|
|
2323
|
+
}
|
|
2324
|
+
throw error;
|
|
2325
|
+
}
|
|
2326
|
+
}
|
|
2327
|
+
return {
|
|
2328
|
+
getRegistryEntries() {
|
|
2329
|
+
return readRegistry(paths);
|
|
2330
|
+
},
|
|
2331
|
+
mountInstalledToolCommands(program) {
|
|
2332
|
+
let entries;
|
|
2333
|
+
try {
|
|
2334
|
+
entries = readRegistry(paths);
|
|
2335
|
+
} catch {
|
|
2336
|
+
return;
|
|
2337
|
+
}
|
|
2338
|
+
const groups = /* @__PURE__ */ new Map();
|
|
2339
|
+
for (const entry of entries) {
|
|
2340
|
+
if (getConflict(entry, entries)) continue;
|
|
2341
|
+
let parent = program;
|
|
2342
|
+
const segments = [];
|
|
2343
|
+
for (const segment of entry.commandPath.slice(0, -1)) {
|
|
2344
|
+
segments.push(segment);
|
|
2345
|
+
const key = segments.join(" ");
|
|
2346
|
+
const group = groups.get(key) ?? new Command8(segment).description("Installed MoneyOS tool commands");
|
|
2347
|
+
if (!groups.has(key)) {
|
|
2348
|
+
groups.set(key, group);
|
|
2349
|
+
parent.addCommand(group);
|
|
2350
|
+
}
|
|
2351
|
+
parent = group;
|
|
2352
|
+
}
|
|
2353
|
+
parent.addCommand(
|
|
2354
|
+
new Command8(entry.commandPath[entry.commandPath.length - 1]).description(entry.description).allowUnknownOption(true).allowExcessArguments(true).helpOption(false).argument("[args...]").action(async (args) => {
|
|
2355
|
+
await invoke(entry, args);
|
|
2356
|
+
})
|
|
2357
|
+
);
|
|
2358
|
+
}
|
|
2359
|
+
},
|
|
2360
|
+
async addTool(input) {
|
|
2361
|
+
const spec = FIRST_PARTY_TOOL_ALIASES[input] ?? input;
|
|
2362
|
+
const entries = readRegistry(paths);
|
|
2363
|
+
const previous = resolveInstalledTool(input, entries);
|
|
2364
|
+
ensureToolHome(paths);
|
|
2365
|
+
const before = JSON.parse(readFileSync4(paths.packageJsonPath, "utf8")).dependencies ?? {};
|
|
2366
|
+
await install(paths, spec);
|
|
2367
|
+
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));
|
|
2369
|
+
try {
|
|
2370
|
+
const loaded = await loadInstalledTool(paths, changed ?? spec);
|
|
2371
|
+
const next = previous ? entries.filter((entry2) => entry2.packageName !== previous.packageName) : entries;
|
|
2372
|
+
const conflict = getConflict(loaded, next);
|
|
2373
|
+
if (conflict) throw new Error(conflict);
|
|
2374
|
+
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]);
|
|
2376
|
+
return entry;
|
|
2377
|
+
} catch (error) {
|
|
2378
|
+
try {
|
|
2379
|
+
if (previous) await install(paths, `${previous.packageName}@${previous.packageVersion}`);
|
|
2380
|
+
else if (changed) await uninstall(paths, changed);
|
|
2381
|
+
} catch {
|
|
2382
|
+
}
|
|
2383
|
+
throw error;
|
|
2384
|
+
}
|
|
2385
|
+
},
|
|
2386
|
+
async removeTool(input) {
|
|
2387
|
+
const entries = readRegistry(paths);
|
|
2388
|
+
const entry = resolveInstalledTool(input, entries);
|
|
2389
|
+
if (!entry) throw new Error(`Tool ${input} is not installed.`);
|
|
2390
|
+
await uninstall(paths, entry.packageName);
|
|
2391
|
+
writeRegistry(paths, entries.filter((installed) => installed.packageName !== entry.packageName));
|
|
2392
|
+
return entry;
|
|
2393
|
+
},
|
|
2394
|
+
async listTools() {
|
|
2395
|
+
const entries = readRegistry(paths);
|
|
2396
|
+
const tools = await Promise.all(entries.map(async (entry) => {
|
|
2397
|
+
const conflict = getConflict(entry, entries);
|
|
2398
|
+
if (conflict) return { ...entry, state: "conflict", problems: [conflict] };
|
|
2399
|
+
try {
|
|
2400
|
+
const loaded = await loadInstalledTool(paths, entry.packageName);
|
|
2401
|
+
if (!sameMetadata(entry, loaded)) throw new Error("installed metadata does not match registry");
|
|
2402
|
+
return { ...entry, state: "ok", problems: [] };
|
|
2403
|
+
} catch (error) {
|
|
2404
|
+
return { ...entry, state: "broken", problems: [error instanceof Error ? error.message : String(error)] };
|
|
2405
|
+
}
|
|
2406
|
+
}));
|
|
2407
|
+
return tools.sort((left, right) => left.commandPath.join(" ").localeCompare(right.commandPath.join(" ")));
|
|
2408
|
+
}
|
|
2409
|
+
};
|
|
2410
|
+
}
|
|
2411
|
+
function formatToolStatusTable(tools) {
|
|
2412
|
+
if (tools.length === 0) return "No tools installed.";
|
|
2413
|
+
const commandWidth = Math.max("COMMAND".length, ...tools.map((tool) => tool.commandPath.join(" ").length));
|
|
2414
|
+
const packageWidth = Math.max("PACKAGE".length, ...tools.map((tool) => tool.packageName.length));
|
|
2415
|
+
const versionWidth = Math.max("VERSION".length, ...tools.map((tool) => tool.packageVersion.length));
|
|
2416
|
+
const lines = [`${"COMMAND".padEnd(commandWidth)} ${"PACKAGE".padEnd(packageWidth)} ${"VERSION".padEnd(versionWidth)} STATE`];
|
|
2417
|
+
for (const tool of tools) {
|
|
2418
|
+
lines.push(`${tool.commandPath.join(" ").padEnd(commandWidth)} ${tool.packageName.padEnd(packageWidth)} ${tool.packageVersion.padEnd(versionWidth)} ${tool.state}`);
|
|
2419
|
+
for (const problem of tool.problems) lines.push(` ${problem}`);
|
|
2420
|
+
}
|
|
2421
|
+
return lines.join("\n");
|
|
2422
|
+
}
|
|
2423
|
+
|
|
2424
|
+
// src/cli/commands/tools.ts
|
|
2425
|
+
function formatError(error) {
|
|
2426
|
+
return error instanceof Error ? error.message : String(error);
|
|
2427
|
+
}
|
|
2428
|
+
function createAddToolCommand(toolManager) {
|
|
2429
|
+
return new Command9("add").description("Install or update a MoneyOS CLI tool into the user tool home").argument("<tool>", "Tool alias or npm package spec").action(async (tool) => {
|
|
2430
|
+
try {
|
|
2431
|
+
const entry = await toolManager.addTool(tool);
|
|
2432
|
+
console.log(
|
|
2433
|
+
`Installed ${entry.packageName}@${entry.packageVersion} as \`moneyos ${entry.commandPath.join(" ")}\`.`
|
|
2434
|
+
);
|
|
2435
|
+
} catch (error) {
|
|
2436
|
+
console.error(formatError(error));
|
|
2437
|
+
process.exitCode = 1;
|
|
2438
|
+
}
|
|
2439
|
+
});
|
|
2440
|
+
}
|
|
2441
|
+
function createRemoveToolCommand(toolManager) {
|
|
2442
|
+
return new Command9("remove").description("Remove an installed MoneyOS CLI tool from the user tool home").argument("<tool>", "Installed tool name, command path, or npm package").action(async (tool) => {
|
|
2443
|
+
try {
|
|
2444
|
+
const entry = await toolManager.removeTool(tool);
|
|
2445
|
+
console.log(
|
|
2446
|
+
`Removed ${entry.packageName} from \`moneyos ${entry.commandPath.join(" ")}\`.`
|
|
2447
|
+
);
|
|
2448
|
+
} catch (error) {
|
|
2449
|
+
console.error(formatError(error));
|
|
2450
|
+
process.exitCode = 1;
|
|
2451
|
+
}
|
|
2452
|
+
});
|
|
2453
|
+
}
|
|
2454
|
+
function createToolsCommand(toolManager) {
|
|
2455
|
+
return new Command9("tools").description("List installed MoneyOS CLI tools from the registry").action(async () => {
|
|
2456
|
+
try {
|
|
2457
|
+
console.log(formatToolStatusTable(await toolManager.listTools()));
|
|
2458
|
+
} catch (error) {
|
|
2459
|
+
console.error(formatError(error));
|
|
2460
|
+
process.exitCode = 1;
|
|
2461
|
+
}
|
|
2462
|
+
});
|
|
2463
|
+
}
|
|
2464
|
+
|
|
2091
2465
|
// src/cli/version.ts
|
|
2092
|
-
import { readFileSync as
|
|
2466
|
+
import { readFileSync as readFileSync5 } from "fs";
|
|
2093
2467
|
import { fileURLToPath } from "url";
|
|
2094
|
-
import { dirname as
|
|
2468
|
+
import { dirname as dirname5, resolve as resolve2 } from "path";
|
|
2095
2469
|
var packageJsonPath = resolve2(
|
|
2096
|
-
|
|
2470
|
+
dirname5(fileURLToPath(import.meta.url)),
|
|
2097
2471
|
"../../package.json"
|
|
2098
2472
|
);
|
|
2099
2473
|
var version = JSON.parse(
|
|
2100
|
-
|
|
2474
|
+
readFileSync5(packageJsonPath, "utf8")
|
|
2101
2475
|
).version;
|
|
2102
2476
|
|
|
2103
2477
|
// src/cli/index.ts
|
|
2104
|
-
function createProgram() {
|
|
2105
|
-
const program = new
|
|
2478
|
+
function createProgram(options = {}) {
|
|
2479
|
+
const program = new Command10();
|
|
2480
|
+
const toolManager = options.toolManager ?? createCliToolManager();
|
|
2106
2481
|
program.name("moneyos").description("The operating system for money").version(version);
|
|
2107
2482
|
program.addCommand(initCommand);
|
|
2108
2483
|
program.addCommand(balanceCommand);
|
|
@@ -2110,20 +2485,33 @@ function createProgram() {
|
|
|
2110
2485
|
program.addCommand(keystoreCommand);
|
|
2111
2486
|
program.addCommand(authCommand);
|
|
2112
2487
|
program.addCommand(backupCommand);
|
|
2488
|
+
program.addCommand(createAddToolCommand(toolManager));
|
|
2489
|
+
program.addCommand(createRemoveToolCommand(toolManager));
|
|
2490
|
+
program.addCommand(createToolsCommand(toolManager));
|
|
2491
|
+
toolManager.mountInstalledToolCommands(program);
|
|
2113
2492
|
program.command("__session-daemon", { hidden: true }).action(async () => {
|
|
2114
2493
|
await runSessionDaemonProcess();
|
|
2115
2494
|
});
|
|
2116
2495
|
return program;
|
|
2117
2496
|
}
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2497
|
+
function resolveEntrypointPath(path) {
|
|
2498
|
+
try {
|
|
2499
|
+
return realpathSync(path);
|
|
2500
|
+
} catch {
|
|
2501
|
+
return resolve3(path);
|
|
2502
|
+
}
|
|
2503
|
+
}
|
|
2504
|
+
function isEntrypointPath(cliEntry, moduleUrl) {
|
|
2505
|
+
return typeof cliEntry === "string" && resolveEntrypointPath(cliEntry) === fileURLToPath2(moduleUrl);
|
|
2506
|
+
}
|
|
2507
|
+
if (isEntrypointPath(process.argv[1], import.meta.url)) {
|
|
2121
2508
|
void createProgram().parseAsync(process.argv).catch((error) => {
|
|
2122
2509
|
console.error(error instanceof Error ? error.message : String(error));
|
|
2123
2510
|
process.exitCode = 1;
|
|
2124
2511
|
});
|
|
2125
2512
|
}
|
|
2126
2513
|
export {
|
|
2127
|
-
createProgram
|
|
2514
|
+
createProgram,
|
|
2515
|
+
isEntrypointPath
|
|
2128
2516
|
};
|
|
2129
2517
|
//# sourceMappingURL=index.js.map
|