remote-dsh 0.5.0 → 0.6.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.
Files changed (2) hide show
  1. package/dist/bin.js +83 -4
  2. package/package.json +3 -3
package/dist/bin.js CHANGED
@@ -25,7 +25,8 @@ Usage:
25
25
  rdsh host leave Unregister this machine from the hub
26
26
  rdsh host user ... Manage gateway users (LAN/cloud password auth)
27
27
  rdsh hub serve Start the hub server (cloud, multi-host)
28
- rdsh hub user add|passwd|rm|ls
28
+ rdsh hub user add|passwd|rm|ls|unlock|reset-2fa
29
+ rdsh hub audit ls Audit log (login/2FA/sharing/email events)
29
30
  rdsh hub host ls|revoke List / revoke hosts (revoke drops the tunnel instantly)
30
31
  rdsh hub service ... Run hub as a systemd/launchd service
31
32
 
@@ -60,7 +61,8 @@ Run the hub server or manage it.
60
61
 
61
62
  Subcommands:
62
63
  serve Start the hub server (TLS required)
63
- user add|passwd|rm|ls Manage users (registration closed; admin creates accounts)
64
+ user add|passwd|rm|ls|unlock|reset-2fa Manage users (admin creates accounts; unlock / reset-2fa)
65
+ audit ls [--user <n>] [--event <e>] [--since 24h|7d] Query the audit log
64
66
  host ls|revoke List / revoke hosts (revoke drops the tunnel instantly)
65
67
  service install|status|uninstall Run hub as a systemd/launchd service
66
68
 
@@ -436,6 +438,56 @@ async function openHubDb(configPath) {
436
438
  const config = await loadHubConfig(hubConfigPath);
437
439
  return new HubDb(config.dbPath);
438
440
  }
441
+ /** `rdsh hub audit ls [--user <name>] [--event <e>] [--since 24h|7d]`。 */
442
+ async function handleHubAudit(args, configPath) {
443
+ const db = await openHubDb(configPath);
444
+ try {
445
+ // 兼容 `audit ls` 与 `audit`(ls 为可选位置参数)
446
+ const opts = args[0] === "ls" ? args.slice(1) : args;
447
+ const filter = {};
448
+ for (let i = 0; i < opts.length; i++) {
449
+ const flag = opts[i];
450
+ if (flag === "--user") {
451
+ const name = opts[++i];
452
+ if (name === undefined)
453
+ throw new Error("missing value for --user");
454
+ const u = db.getUserByName(name);
455
+ if (u === null)
456
+ throw new Error(`user '${name}' not found`);
457
+ filter.userId = u.id;
458
+ }
459
+ else if (flag === "--event") {
460
+ filter.event = opts[++i];
461
+ if (filter.event === undefined)
462
+ throw new Error("missing value for --event");
463
+ }
464
+ else if (flag === "--since") {
465
+ const v = opts[++i];
466
+ if (v === undefined)
467
+ throw new Error("missing value for --since");
468
+ const m = /^(\d+)([hd])$/.exec(v);
469
+ if (m === null)
470
+ throw new Error(`invalid --since '${v}' (use like 24h or 7d)`);
471
+ filter.since = Date.now() - Number(m[1]) * (m[2] === "h" ? 3_600_000 : 86_400_000);
472
+ }
473
+ else {
474
+ throw new Error(`unknown option '${flag}'`);
475
+ }
476
+ }
477
+ const events = db.listAudit(filter);
478
+ if (events.length === 0) {
479
+ console.log("(no events)");
480
+ return;
481
+ }
482
+ for (const e of events) {
483
+ const user = e.userId !== null ? db.getUserById(e.userId) : null;
484
+ console.log(`${new Date(e.createdAt).toISOString()} ${user?.name ?? "-"} ${e.event} ip=${e.ip} ${e.detailJson}`);
485
+ }
486
+ }
487
+ finally {
488
+ db.close();
489
+ }
490
+ }
439
491
  async function handleHub(args, configPath) {
440
492
  const sub = args[0];
441
493
  const rest = args.slice(1);
@@ -457,6 +509,10 @@ async function handleHub(args, configPath) {
457
509
  await handleHubHost(rest, configPath);
458
510
  return;
459
511
  }
512
+ case "audit": {
513
+ await handleHubAudit(rest, configPath);
514
+ return;
515
+ }
460
516
  case "service": {
461
517
  const action = rest[0];
462
518
  const hubConfigPath = resolveHubConfigPath(configPath);
@@ -476,7 +532,7 @@ async function handleHub(args, configPath) {
476
532
  }
477
533
  }
478
534
  default:
479
- throw new Error("usage: rdsh hub serve|user|host|service");
535
+ throw new Error("usage: rdsh hub serve|user|host|audit|service");
480
536
  }
481
537
  }
482
538
  function parseHubServeArgs(args, configPath) {
@@ -572,8 +628,31 @@ async function handleHubUser(args, configPath) {
572
628
  console.log(`rdsh: user '${name}' removed (their hosts removed)`);
573
629
  return;
574
630
  }
631
+ case "unlock": {
632
+ const name = args[1];
633
+ if (name === undefined)
634
+ throw new Error("usage: rdsh hub user unlock <name>");
635
+ const user = db.getUserByName(name);
636
+ if (user === null)
637
+ throw new Error(`user '${name}' not found`);
638
+ db.unlockAccount(user.id);
639
+ console.log(`rdsh: user '${name}' unlocked`);
640
+ return;
641
+ }
642
+ case "reset-2fa": {
643
+ const name = args[1];
644
+ if (name === undefined)
645
+ throw new Error("usage: rdsh hub user reset-2fa <name>");
646
+ const user = db.getUserByName(name);
647
+ if (user === null)
648
+ throw new Error(`user '${name}' not found`);
649
+ db.clearTotpSecret(user.id);
650
+ db.bumpVersion(user.id);
651
+ console.log(`rdsh: 2FA reset for '${name}' (their sessions revoked)`);
652
+ return;
653
+ }
575
654
  default:
576
- throw new Error("usage: rdsh hub user add|passwd|ls|rm");
655
+ throw new Error("usage: rdsh hub user add|passwd|ls|rm|unlock|reset-2fa");
577
656
  }
578
657
  }
579
658
  finally {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "remote-dsh",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "Secure remote access for DeepSeek Harness: rdsh serve / rdsh join / rdsh hub",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -23,8 +23,8 @@
23
23
  "node": ">=22"
24
24
  },
25
25
  "dependencies": {
26
- "rdsh-gateway": "0.3.0",
27
- "rdsh-hub": "0.3.0"
26
+ "rdsh-gateway": "0.4.0",
27
+ "rdsh-hub": "0.4.0"
28
28
  },
29
29
  "scripts": {
30
30
  "build": "tsc -p tsconfig.json",