moneyos 0.3.2 → 0.3.4
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 +15 -3
- package/dist/cli/index.js +195 -47
- package/dist/cli/index.js.map +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -3
- package/dist/index.d.ts +2 -3
- package/dist/index.js.map +1 -1
- package/package.json +2 -9
package/README.md
CHANGED
|
@@ -12,7 +12,6 @@ but the repo is structured so each major surface can evolve independently.
|
|
|
12
12
|
- `moneyos`: the root SDK + CLI package
|
|
13
13
|
- `@moneyos/core`: runtime interfaces, shared types, chain/token registries
|
|
14
14
|
- `@moneyos/tool-swap`: swap execution tool and provider surface
|
|
15
|
-
- `@moneyos/executor-particle`: Particle AA smart-account executor
|
|
16
15
|
|
|
17
16
|
## CLI
|
|
18
17
|
|
|
@@ -23,6 +22,7 @@ moneyos init [--key 0x...] [--force] [--chain 42161] [--rpc https://...]
|
|
|
23
22
|
moneyos auth unlock
|
|
24
23
|
moneyos auth lock
|
|
25
24
|
moneyos auth status
|
|
25
|
+
moneyos auth change-password
|
|
26
26
|
moneyos backup export [--out ./wallet-backup.json] [--force]
|
|
27
27
|
moneyos backup restore <path> [--force]
|
|
28
28
|
moneyos backup status
|
|
@@ -79,6 +79,7 @@ What is landed in code today:
|
|
|
79
79
|
- `~/.moneyos/config.json` now stores only non-secret settings such as chain and RPC configuration
|
|
80
80
|
- `MONEYOS_PRIVATE_KEY` remains an explicit override for ephemeral CI or agent runs
|
|
81
81
|
- `moneyos auth unlock` opens a short-lived local session for write commands
|
|
82
|
+
- `moneyos auth change-password` rotates the local wallet password and locks the current session
|
|
82
83
|
- `moneyos backup export|restore|status` manages encrypted wallet backups
|
|
83
84
|
- Normal wallet commands resolve their write path through one shared session-aware flow
|
|
84
85
|
- Local EOA signers use viem's nonce manager, so back-to-back live transactions
|
|
@@ -120,6 +121,19 @@ If you have a legacy `~/.moneyos/config.json` with a plaintext `privateKey`
|
|
|
120
121
|
field from a pre-encrypted-wallet version of the CLI, `moneyos init` with no
|
|
121
122
|
flags will detect and import that key automatically.
|
|
122
123
|
|
|
124
|
+
## Changing the wallet password
|
|
125
|
+
|
|
126
|
+
Use:
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
moneyos auth change-password
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
This re-encrypts the active local wallet file with a new password and locks the
|
|
133
|
+
current local session. Existing backup files and previously exported backups
|
|
134
|
+
remain snapshots encrypted with the old password. If you want a backup under
|
|
135
|
+
the new password, run `moneyos backup export` after the password change.
|
|
136
|
+
|
|
123
137
|
## Threat model
|
|
124
138
|
|
|
125
139
|
What this wallet model is meant to protect against:
|
|
@@ -186,7 +200,6 @@ What still needs more hands-on validation:
|
|
|
186
200
|
- live session-backed ETH send
|
|
187
201
|
- live session-backed ERC-20 send
|
|
188
202
|
- live session-backed swap
|
|
189
|
-
- Particle executor against real infrastructure
|
|
190
203
|
- more live usage of the encrypted-wallet/auth/backup flow in a real terminal
|
|
191
204
|
|
|
192
205
|
## Development
|
|
@@ -195,7 +208,6 @@ What still needs more hands-on validation:
|
|
|
195
208
|
npm install
|
|
196
209
|
npm run build:core
|
|
197
210
|
npm run build:tool-swap
|
|
198
|
-
npm run build:executor-particle
|
|
199
211
|
npm run typecheck
|
|
200
212
|
npm test
|
|
201
213
|
npm run lint
|
package/dist/cli/index.js
CHANGED
|
@@ -47,6 +47,10 @@ var DEFAULT_KDF = {
|
|
|
47
47
|
};
|
|
48
48
|
var SECURE_FILE_MODE = 384;
|
|
49
49
|
var SECURE_PARENT_MODE = 448;
|
|
50
|
+
var WALLET_WRITE_LABELS = {
|
|
51
|
+
parentDescription: "Wallet directory",
|
|
52
|
+
fileDescription: "Wallet file"
|
|
53
|
+
};
|
|
50
54
|
function normalizePassphrase(passphrase) {
|
|
51
55
|
return passphrase.normalize("NFC");
|
|
52
56
|
}
|
|
@@ -59,7 +63,7 @@ function walletProofMessage(wallet) {
|
|
|
59
63
|
`createdAt=${wallet.createdAt}`
|
|
60
64
|
].join("|");
|
|
61
65
|
}
|
|
62
|
-
function ensureParentDir(path) {
|
|
66
|
+
function ensureParentDir(path, label) {
|
|
63
67
|
const dir = dirname(path);
|
|
64
68
|
if (!existsSync(dir)) {
|
|
65
69
|
mkdirSync(dir, { recursive: true, mode: SECURE_PARENT_MODE });
|
|
@@ -68,7 +72,7 @@ function ensureParentDir(path) {
|
|
|
68
72
|
const mode = statSync(dir).mode & 511;
|
|
69
73
|
if ((mode & 63) !== 0) {
|
|
70
74
|
throw new Error(
|
|
71
|
-
|
|
75
|
+
`${label} ${dir} has insecure permissions (${mode.toString(8)}). Restrict it to 700 before continuing.`
|
|
72
76
|
);
|
|
73
77
|
}
|
|
74
78
|
}
|
|
@@ -161,9 +165,9 @@ function walletAad(kdf) {
|
|
|
161
165
|
"utf8"
|
|
162
166
|
);
|
|
163
167
|
}
|
|
164
|
-
function writeFileAtomicSecure(path, contents) {
|
|
165
|
-
ensureParentDir(path);
|
|
166
|
-
assertSecureFileMode(path,
|
|
168
|
+
function writeFileAtomicSecure(path, contents, labels = WALLET_WRITE_LABELS) {
|
|
169
|
+
ensureParentDir(path, labels.parentDescription);
|
|
170
|
+
assertSecureFileMode(path, labels.fileDescription);
|
|
167
171
|
const tmpPath = join(
|
|
168
172
|
dirname(path),
|
|
169
173
|
`.${basename(path)}.${randomBytes(6).toString("hex")}.tmp`
|
|
@@ -190,7 +194,7 @@ function writeFileAtomicSecure(path, contents) {
|
|
|
190
194
|
}
|
|
191
195
|
async function encryptWallet(params) {
|
|
192
196
|
const account = privateKeyToAccount(params.privateKey);
|
|
193
|
-
const createdAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
197
|
+
const createdAt = params.identity?.createdAt ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
194
198
|
const salt = randomBytes(16);
|
|
195
199
|
const nonce = randomBytes(12);
|
|
196
200
|
const key = deriveKey(params.passphrase, salt, DEFAULT_KDF);
|
|
@@ -202,7 +206,7 @@ async function encryptWallet(params) {
|
|
|
202
206
|
);
|
|
203
207
|
const ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]);
|
|
204
208
|
const authTag = cipher.getAuthTag();
|
|
205
|
-
const addressProof = await account.signMessage({
|
|
209
|
+
const addressProof = params.identity?.addressProof ?? await account.signMessage({
|
|
206
210
|
message: walletProofMessage({
|
|
207
211
|
version: 1,
|
|
208
212
|
kind: "encrypted-local-eoa",
|
|
@@ -210,10 +214,13 @@ async function encryptWallet(params) {
|
|
|
210
214
|
createdAt
|
|
211
215
|
})
|
|
212
216
|
});
|
|
217
|
+
if (params.identity && account.address.toLowerCase() !== params.identity.address.toLowerCase()) {
|
|
218
|
+
throw new Error("wallet address metadata mismatch");
|
|
219
|
+
}
|
|
213
220
|
return {
|
|
214
|
-
version: 1,
|
|
215
|
-
kind: "encrypted-local-eoa",
|
|
216
|
-
address: account.address,
|
|
221
|
+
version: params.identity?.version ?? 1,
|
|
222
|
+
kind: params.identity?.kind ?? "encrypted-local-eoa",
|
|
223
|
+
address: params.identity?.address ?? account.address,
|
|
217
224
|
createdAt,
|
|
218
225
|
addressProof,
|
|
219
226
|
kdf: DEFAULT_KDF,
|
|
@@ -226,6 +233,15 @@ async function encryptWallet(params) {
|
|
|
226
233
|
}
|
|
227
234
|
};
|
|
228
235
|
}
|
|
236
|
+
function walletIdentity(wallet) {
|
|
237
|
+
return {
|
|
238
|
+
version: wallet.version,
|
|
239
|
+
kind: wallet.kind,
|
|
240
|
+
address: wallet.address,
|
|
241
|
+
createdAt: wallet.createdAt,
|
|
242
|
+
addressProof: wallet.addressProof
|
|
243
|
+
};
|
|
244
|
+
}
|
|
229
245
|
async function decryptWalletFile(wallet, passphrase) {
|
|
230
246
|
try {
|
|
231
247
|
const salt = Buffer.from(wallet.crypto.salt, "base64");
|
|
@@ -276,9 +292,32 @@ var FileEncryptedWalletStore = class {
|
|
|
276
292
|
}
|
|
277
293
|
async save(params) {
|
|
278
294
|
const wallet = await encryptWallet(params);
|
|
279
|
-
writeFileAtomicSecure(
|
|
295
|
+
writeFileAtomicSecure(
|
|
296
|
+
this.walletPath,
|
|
297
|
+
JSON.stringify(wallet, null, 2),
|
|
298
|
+
WALLET_WRITE_LABELS
|
|
299
|
+
);
|
|
280
300
|
return toMetadata(wallet);
|
|
281
301
|
}
|
|
302
|
+
async rotatePassphrase(params) {
|
|
303
|
+
if (!this.exists()) {
|
|
304
|
+
throw new Error("No wallet configured. Run `moneyos init`.");
|
|
305
|
+
}
|
|
306
|
+
assertSecureFileMode(this.walletPath, "Wallet file");
|
|
307
|
+
const wallet = parseWalletFile(
|
|
308
|
+
readFileSync(this.walletPath, "utf8"),
|
|
309
|
+
this.walletPath
|
|
310
|
+
);
|
|
311
|
+
await verifyWalletAddressProof(wallet, this.walletPath);
|
|
312
|
+
const privateKey = await decryptWalletFile(wallet, params.oldPassphrase);
|
|
313
|
+
const rotated = await encryptWallet({
|
|
314
|
+
privateKey,
|
|
315
|
+
passphrase: params.newPassphrase,
|
|
316
|
+
identity: walletIdentity(wallet)
|
|
317
|
+
});
|
|
318
|
+
writeFileAtomicSecure(this.walletPath, JSON.stringify(rotated, null, 2));
|
|
319
|
+
return toMetadata(rotated);
|
|
320
|
+
}
|
|
282
321
|
async decrypt(passphrase) {
|
|
283
322
|
if (!this.exists()) {
|
|
284
323
|
throw new Error("No wallet configured. Run `moneyos init`.");
|
|
@@ -306,10 +345,20 @@ var FileEncryptedWalletStore = class {
|
|
|
306
345
|
async restore(data) {
|
|
307
346
|
const wallet = parseWalletFile(JSON.stringify(data), this.walletPath);
|
|
308
347
|
await verifyWalletAddressProof(wallet, this.walletPath);
|
|
309
|
-
writeFileAtomicSecure(
|
|
348
|
+
writeFileAtomicSecure(
|
|
349
|
+
this.walletPath,
|
|
350
|
+
JSON.stringify(wallet, null, 2),
|
|
351
|
+
WALLET_WRITE_LABELS
|
|
352
|
+
);
|
|
310
353
|
return toMetadata(wallet);
|
|
311
354
|
}
|
|
312
355
|
};
|
|
356
|
+
async function writeEncryptedWalletFile(path, data, labels = WALLET_WRITE_LABELS) {
|
|
357
|
+
const wallet = parseWalletFile(JSON.stringify(data), path);
|
|
358
|
+
await verifyWalletAddressProof(wallet, path);
|
|
359
|
+
writeFileAtomicSecure(path, JSON.stringify(wallet, null, 2), labels);
|
|
360
|
+
return toMetadata(wallet);
|
|
361
|
+
}
|
|
313
362
|
async function readEncryptedWalletFile(path) {
|
|
314
363
|
assertSecureFileMode(path, "Wallet file");
|
|
315
364
|
const wallet = parseWalletFile(readFileSync(path, "utf8"), path);
|
|
@@ -360,7 +409,10 @@ var FileBackupProvider = class {
|
|
|
360
409
|
"Backup file already exists. Re-run with `--force` if you really want to overwrite it."
|
|
361
410
|
);
|
|
362
411
|
}
|
|
363
|
-
await
|
|
412
|
+
await writeEncryptedWalletFile(targetPath, wallet, {
|
|
413
|
+
parentDescription: "Backup export destination directory",
|
|
414
|
+
fileDescription: "Backup export file"
|
|
415
|
+
});
|
|
364
416
|
return targetPath;
|
|
365
417
|
}
|
|
366
418
|
async restoreWallet(fromPath, options) {
|
|
@@ -1259,38 +1311,58 @@ function parseChainId(value, fallback) {
|
|
|
1259
1311
|
}
|
|
1260
1312
|
return parsed;
|
|
1261
1313
|
}
|
|
1262
|
-
var
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1314
|
+
var defaultInitCommandDependencies = {
|
|
1315
|
+
loadFileConfig,
|
|
1316
|
+
getWalletPath,
|
|
1317
|
+
getBackupDir,
|
|
1318
|
+
getConfigPath,
|
|
1319
|
+
createWalletStore: (walletPath) => new FileEncryptedWalletStore(walletPath),
|
|
1320
|
+
createBackupProvider: (params) => new FileBackupProvider(params),
|
|
1321
|
+
hasRemovedOnePasswordConfig,
|
|
1322
|
+
getRemovedOnePasswordStorageMessage,
|
|
1323
|
+
hasLegacyPlaintextWalletConfig,
|
|
1324
|
+
getLegacyPlaintextWalletStorageMessage,
|
|
1325
|
+
promptHidden,
|
|
1326
|
+
lockSession,
|
|
1327
|
+
getSessionSocketPath,
|
|
1328
|
+
getSessionTokenPath,
|
|
1329
|
+
saveConfig,
|
|
1330
|
+
generatePrivateKey,
|
|
1331
|
+
log: (message) => console.log(message),
|
|
1332
|
+
error: (message) => console.error(message)
|
|
1333
|
+
};
|
|
1334
|
+
async function runInitCommand(options, deps = defaultInitCommandDependencies) {
|
|
1335
|
+
const existing = deps.loadFileConfig();
|
|
1336
|
+
const walletPath = deps.getWalletPath(existing);
|
|
1337
|
+
const backupDir = deps.getBackupDir(existing);
|
|
1338
|
+
const wallet = deps.createWalletStore(walletPath);
|
|
1267
1339
|
if (wallet.exists() && !options.force) {
|
|
1268
1340
|
const metadata = await wallet.metadata();
|
|
1269
|
-
|
|
1341
|
+
deps.log(`Already initialized.`);
|
|
1270
1342
|
if (metadata?.address) {
|
|
1271
|
-
|
|
1343
|
+
deps.log(`Address: ${metadata.address}`);
|
|
1272
1344
|
}
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1345
|
+
deps.log(`Wallet: ${walletPath}`);
|
|
1346
|
+
deps.log(`Config: ${deps.getConfigPath()}`);
|
|
1347
|
+
deps.log(`
|
|
1276
1348
|
To reinitialize, run: moneyos init --force --key <privateKey>`);
|
|
1277
1349
|
return;
|
|
1278
1350
|
}
|
|
1279
|
-
if (hasRemovedOnePasswordConfig(existing) && !options.key) {
|
|
1280
|
-
|
|
1281
|
-
|
|
1351
|
+
if (deps.hasRemovedOnePasswordConfig(existing) && !options.key) {
|
|
1352
|
+
deps.error(deps.getRemovedOnePasswordStorageMessage());
|
|
1353
|
+
deps.error(`Config: ${deps.getConfigPath()}`);
|
|
1282
1354
|
return;
|
|
1283
1355
|
}
|
|
1284
1356
|
try {
|
|
1285
|
-
const privateKey = options.key ?? (hasLegacyPlaintextWalletConfig(existing) ? existing.privateKey : generatePrivateKey());
|
|
1357
|
+
const privateKey = options.key ?? (deps.hasLegacyPlaintextWalletConfig(existing) ? existing.privateKey : deps.generatePrivateKey());
|
|
1286
1358
|
const account = privateKeyToAccount3(privateKey);
|
|
1287
1359
|
const chainId = parseChainId(options.chain, existing.chainId ?? 42161);
|
|
1288
1360
|
const rpcUrl = options.rpc ?? existing.rpcUrl;
|
|
1289
|
-
const passphrase = await promptHidden("Choose wallet password: ");
|
|
1361
|
+
const passphrase = await deps.promptHidden("Choose wallet password: ");
|
|
1290
1362
|
if (passphrase.length < 8) {
|
|
1291
1363
|
throw new Error("Wallet password must be at least 8 characters long.");
|
|
1292
1364
|
}
|
|
1293
|
-
const confirmPassphrase = await promptHidden("Confirm wallet password: ");
|
|
1365
|
+
const confirmPassphrase = await deps.promptHidden("Confirm wallet password: ");
|
|
1294
1366
|
if (passphrase !== confirmPassphrase) {
|
|
1295
1367
|
throw new Error("Wallet password confirmation did not match.");
|
|
1296
1368
|
}
|
|
@@ -1298,41 +1370,47 @@ To reinitialize, run: moneyos init --force --key <privateKey>`);
|
|
|
1298
1370
|
privateKey,
|
|
1299
1371
|
passphrase
|
|
1300
1372
|
});
|
|
1301
|
-
await lockSession(
|
|
1302
|
-
|
|
1373
|
+
await deps.lockSession(
|
|
1374
|
+
deps.getSessionSocketPath(),
|
|
1375
|
+
deps.getSessionTokenPath()
|
|
1376
|
+
);
|
|
1377
|
+
deps.saveConfig({
|
|
1303
1378
|
chainId,
|
|
1304
1379
|
rpcUrl,
|
|
1305
1380
|
walletPath: existing.walletPath,
|
|
1306
1381
|
backupDir: existing.backupDir
|
|
1307
1382
|
});
|
|
1308
|
-
const backupProvider =
|
|
1383
|
+
const backupProvider = deps.createBackupProvider({
|
|
1309
1384
|
walletPath,
|
|
1310
1385
|
backupDir
|
|
1311
1386
|
});
|
|
1312
1387
|
const backupPath = await backupProvider.exportWallet();
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1388
|
+
deps.log(`MoneyOS initialized.`);
|
|
1389
|
+
deps.log(`Address: ${account.address}`);
|
|
1390
|
+
deps.log(`Wallet: ${walletPath}`);
|
|
1391
|
+
deps.log(`Config: ${deps.getConfigPath()}`);
|
|
1392
|
+
deps.log(`Backup: ${backupPath}`);
|
|
1393
|
+
deps.log(
|
|
1319
1394
|
`
|
|
1320
1395
|
Save your wallet password in your password manager of choice. MoneyOS does not store or sync it for you.`
|
|
1321
1396
|
);
|
|
1322
|
-
if (hasLegacyPlaintextWalletConfig(existing) && !options.key) {
|
|
1323
|
-
|
|
1397
|
+
if (deps.hasLegacyPlaintextWalletConfig(existing) && !options.key) {
|
|
1398
|
+
deps.log(`
|
|
1324
1399
|
Imported legacy plaintext wallet into the encrypted wallet file.`);
|
|
1325
|
-
|
|
1400
|
+
deps.log(deps.getLegacyPlaintextWalletStorageMessage());
|
|
1326
1401
|
} else if (!options.key) {
|
|
1327
|
-
|
|
1402
|
+
deps.log(
|
|
1328
1403
|
`
|
|
1329
1404
|
This is a new account. Fund it before sending transactions.`
|
|
1330
1405
|
);
|
|
1331
1406
|
}
|
|
1332
1407
|
} catch (error) {
|
|
1333
|
-
|
|
1408
|
+
deps.error(error instanceof Error ? error.message : String(error));
|
|
1334
1409
|
process.exitCode = 1;
|
|
1335
1410
|
}
|
|
1411
|
+
}
|
|
1412
|
+
var initCommand = new Command("init").description("Initialize MoneyOS with a new or imported encrypted wallet").option("-k, --key <privateKey>", "Import an existing private key").option("--force", "Overwrite the existing encrypted wallet").option("--chain <chainId>", "Default chain ID (default: 42161 Arbitrum)").option("--rpc <url>", "Custom RPC URL").action(async (options) => {
|
|
1413
|
+
await runInitCommand(options);
|
|
1336
1414
|
});
|
|
1337
1415
|
|
|
1338
1416
|
// src/cli/commands/balance.ts
|
|
@@ -1992,8 +2070,70 @@ function formatSessionStatus(params) {
|
|
|
1992
2070
|
}
|
|
1993
2071
|
return lines.join("\n");
|
|
1994
2072
|
}
|
|
2073
|
+
var defaultChangePasswordCommandDependencies = {
|
|
2074
|
+
loadFileConfig,
|
|
2075
|
+
getWalletPath,
|
|
2076
|
+
createWalletStore: (walletPath) => new FileEncryptedWalletStore(walletPath),
|
|
2077
|
+
promptHidden,
|
|
2078
|
+
lockSession,
|
|
2079
|
+
getSessionSocketPath,
|
|
2080
|
+
getSessionTokenPath,
|
|
2081
|
+
log: (message) => console.log(message),
|
|
2082
|
+
error: (message) => console.error(message)
|
|
2083
|
+
};
|
|
2084
|
+
async function runChangePasswordCommand(deps = defaultChangePasswordCommandDependencies) {
|
|
2085
|
+
const config = deps.loadFileConfig();
|
|
2086
|
+
const walletPath = deps.getWalletPath(config);
|
|
2087
|
+
const wallet = deps.createWalletStore(walletPath);
|
|
2088
|
+
if (!wallet.exists()) {
|
|
2089
|
+
deps.error("No encrypted wallet found. Run `moneyos init` first.");
|
|
2090
|
+
process.exitCode = 1;
|
|
2091
|
+
return;
|
|
2092
|
+
}
|
|
2093
|
+
try {
|
|
2094
|
+
const currentPassphrase = await deps.promptHidden("Current wallet password: ");
|
|
2095
|
+
if (currentPassphrase.length === 0) {
|
|
2096
|
+
throw new Error("Current wallet password cannot be empty.");
|
|
2097
|
+
}
|
|
2098
|
+
const newPassphrase = await deps.promptHidden("New wallet password: ");
|
|
2099
|
+
if (newPassphrase.length < 8) {
|
|
2100
|
+
throw new Error("New wallet password must be at least 8 characters long.");
|
|
2101
|
+
}
|
|
2102
|
+
if (newPassphrase === currentPassphrase) {
|
|
2103
|
+
throw new Error(
|
|
2104
|
+
"New wallet password must differ from the current password."
|
|
2105
|
+
);
|
|
2106
|
+
}
|
|
2107
|
+
const confirmPassphrase = await deps.promptHidden(
|
|
2108
|
+
"Confirm new wallet password: "
|
|
2109
|
+
);
|
|
2110
|
+
if (newPassphrase !== confirmPassphrase) {
|
|
2111
|
+
throw new Error("New wallet password confirmation did not match.");
|
|
2112
|
+
}
|
|
2113
|
+
const metadata = await wallet.rotatePassphrase({
|
|
2114
|
+
oldPassphrase: currentPassphrase,
|
|
2115
|
+
newPassphrase
|
|
2116
|
+
});
|
|
2117
|
+
await deps.lockSession(
|
|
2118
|
+
deps.getSessionSocketPath(),
|
|
2119
|
+
deps.getSessionTokenPath()
|
|
2120
|
+
);
|
|
2121
|
+
deps.log("Wallet password changed.");
|
|
2122
|
+
deps.log(`Address: ${metadata.address}`);
|
|
2123
|
+
deps.log(formatSessionStatus({ state: "locked" }));
|
|
2124
|
+
deps.log(
|
|
2125
|
+
"Existing backup files and exported copies still require the old wallet password."
|
|
2126
|
+
);
|
|
2127
|
+
deps.log(
|
|
2128
|
+
"Run `moneyos backup export` to create a backup encrypted with the new wallet password."
|
|
2129
|
+
);
|
|
2130
|
+
} catch (error) {
|
|
2131
|
+
deps.error(error instanceof Error ? error.message : String(error));
|
|
2132
|
+
process.exitCode = 1;
|
|
2133
|
+
}
|
|
2134
|
+
}
|
|
1995
2135
|
var authCommand = new Command6("auth").description(
|
|
1996
|
-
"Unlock, inspect, and
|
|
2136
|
+
"Unlock, inspect, lock, and change the local MoneyOS wallet password"
|
|
1997
2137
|
);
|
|
1998
2138
|
authCommand.command("unlock").description("Unlock the local wallet and start a short-lived session").action(async () => {
|
|
1999
2139
|
const config = loadFileConfig();
|
|
@@ -2032,6 +2172,9 @@ authCommand.command("unlock").description("Unlock the local wallet and start a s
|
|
|
2032
2172
|
process.exitCode = 1;
|
|
2033
2173
|
}
|
|
2034
2174
|
});
|
|
2175
|
+
authCommand.command("change-password").description("Change the local wallet password and lock the current session").action(async () => {
|
|
2176
|
+
await runChangePasswordCommand();
|
|
2177
|
+
});
|
|
2035
2178
|
authCommand.command("lock").description("Lock the local MoneyOS wallet session").action(async () => {
|
|
2036
2179
|
const locked = await lockSession(
|
|
2037
2180
|
getSessionSocketPath(),
|
|
@@ -2084,6 +2227,13 @@ function formatBackupStatus(params) {
|
|
|
2084
2227
|
}
|
|
2085
2228
|
return lines.join("\n");
|
|
2086
2229
|
}
|
|
2230
|
+
function getBackupExportPasswordGuidance() {
|
|
2231
|
+
return [
|
|
2232
|
+
"This backup is encrypted with the same wallet password as your active wallet.",
|
|
2233
|
+
"You will need that same wallet password to restore it.",
|
|
2234
|
+
"MoneyOS does not store or sync it for you."
|
|
2235
|
+
].join(" ");
|
|
2236
|
+
}
|
|
2087
2237
|
var backupCommand = new Command7("backup").description(
|
|
2088
2238
|
"Export, restore, and inspect encrypted wallet backups"
|
|
2089
2239
|
);
|
|
@@ -2099,9 +2249,7 @@ backupCommand.command("export").description("Write a copy of the encrypted walle
|
|
|
2099
2249
|
allowOverwrite: Boolean(options.force)
|
|
2100
2250
|
});
|
|
2101
2251
|
console.log(`Backup exported to ${targetPath}`);
|
|
2102
|
-
console.log(
|
|
2103
|
-
"Save your wallet password in your password manager of choice. MoneyOS does not store or sync it for you."
|
|
2104
|
-
);
|
|
2252
|
+
console.log(getBackupExportPasswordGuidance());
|
|
2105
2253
|
} catch (error) {
|
|
2106
2254
|
console.error(error instanceof Error ? error.message : String(error));
|
|
2107
2255
|
process.exitCode = 1;
|