remosidian 0.1.1 → 0.1.2

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/remosidian.mjs +382 -38
  2. package/package.json +1 -1
@@ -14380,7 +14380,11 @@ var __webpack_exports__ = {};
14380
14380
 
14381
14381
  // EXPORTS
14382
14382
  __nccwpck_require__.d(__webpack_exports__, {
14383
+ gu: () => (/* binding */ buildPairCode),
14384
+ ti: () => (/* binding */ buildPairDeepLink),
14383
14385
  lz: () => (/* binding */ buildPairUri),
14386
+ jO: () => (/* binding */ buildServeArgs),
14387
+ YP: () => (/* binding */ createCompactPairingPayload),
14384
14388
  wX: () => (/* binding */ expandVaultSpec),
14385
14389
  iW: () => (/* binding */ main),
14386
14390
  Zy: () => (/* binding */ parseArgs),
@@ -14388,6 +14392,8 @@ __nccwpck_require__.d(__webpack_exports__, {
14388
14392
  t0: () => (/* binding */ resolveInvocation)
14389
14393
  });
14390
14394
 
14395
+ ;// CONCATENATED MODULE: external "node:child_process"
14396
+ const external_node_child_process_namespaceObject = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:child_process");
14391
14397
  ;// CONCATENATED MODULE: external "node:fs"
14392
14398
  const external_node_fs_namespaceObject = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:fs");
14393
14399
  ;// CONCATENATED MODULE: external "node:os"
@@ -14713,8 +14719,6 @@ function loadOrCreateDaemonIdentity(port, identityPath) {
14713
14719
  return identity;
14714
14720
  }
14715
14721
  //# sourceMappingURL=identity.js.map
14716
- ;// CONCATENATED MODULE: external "node:child_process"
14717
- const external_node_child_process_namespaceObject = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:child_process");
14718
14722
  ;// CONCATENATED MODULE: ../../packages/daemon-core/dist/advertised-hosts.js
14719
14723
 
14720
14724
 
@@ -19581,7 +19585,7 @@ function runFileBackedPluginTurn(input, prompt, options, onDelta) {
19581
19585
  }
19582
19586
  //# sourceMappingURL=chat-plugin-runtime.js.map
19583
19587
  ;// CONCATENATED MODULE: ../remosidian-server/dist/version.js
19584
- const VERSION = '0.1.1';
19588
+ const VERSION = '0.1.2';
19585
19589
  //# sourceMappingURL=version.js.map
19586
19590
  ;// CONCATENATED MODULE: ../remosidian-server/dist/chat-runtime.js
19587
19591
 
@@ -28063,6 +28067,7 @@ function startTunnel({ port, logDir, tunnelStatePath, resolveLaunchSpec = resolv
28063
28067
 
28064
28068
 
28065
28069
 
28070
+
28066
28071
  const PROG = 'remosidian';
28067
28072
  const PRIMARY_NPM_PACKAGE = '@remosidian/cli';
28068
28073
  const ALIAS_NPM_PACKAGE = 'remosidian';
@@ -28081,9 +28086,11 @@ function helpText() {
28081
28086
  ` ${PROG} <command> [options]`,
28082
28087
  '',
28083
28088
  'Commands:',
28084
- ' start Start the Remosidian server in the foreground',
28089
+ ' start Start or reuse the Remosidian background daemon',
28090
+ ' serve Internal foreground daemon entrypoint',
28085
28091
  ' pair Open a one-time pairing window and print a QR code',
28086
28092
  ' status Print the running daemon health snapshot',
28093
+ ' stop Stop the background daemon',
28087
28094
  ' reset-pairings Revoke every paired device (local only)',
28088
28095
  ' init Create ~/.remosidian/config.json interactively',
28089
28096
  ' help Show this help',
@@ -28105,6 +28112,7 @@ function helpText() {
28105
28112
  ' --bind <address> Bind to this interface (default 0.0.0.0)',
28106
28113
  ' --agent <name> AI agent for /Ask: claude or codex (auto-detects)',
28107
28114
  ' --agent-path <path> Override the agent binary path',
28115
+ ' --foreground Run the server in the foreground',
28108
28116
  ' --json Emit a single JSON line instead of human output',
28109
28117
  ' --no-qr Skip the pairing QR on startup',
28110
28118
  ].join('\n');
@@ -28211,6 +28219,69 @@ function resolvePaths(flags) {
28211
28219
  return createRemosidianPaths(dataDir ?? process.env.REMOSIDIAN_HOME ?? '~/.remosidian');
28212
28220
  }
28213
28221
 
28222
+ function createRuntimePaths(paths) {
28223
+ const runtimeDir = (0,external_node_path_namespaceObject.join)(paths.dataDir, 'runtime', 'current');
28224
+ const logDir = (0,external_node_path_namespaceObject.join)(paths.dataDir, 'logs');
28225
+ return {
28226
+ runtimeDir,
28227
+ runtimeEntryPath: (0,external_node_path_namespaceObject.join)(runtimeDir, 'dist', 'remosidian.mjs'),
28228
+ pidPath: (0,external_node_path_namespaceObject.join)(paths.dataDir, 'daemon.pid'),
28229
+ logDir,
28230
+ stdoutLogPath: (0,external_node_path_namespaceObject.join)(logDir, 'daemon.stdout.log'),
28231
+ stderrLogPath: (0,external_node_path_namespaceObject.join)(logDir, 'daemon.stderr.log'),
28232
+ pairingQrPath: (0,external_node_path_namespaceObject.join)(paths.dataDir, 'pairing-qr.png'),
28233
+ pairingCodePath: (0,external_node_path_namespaceObject.join)(paths.dataDir, 'pairing-code.txt'),
28234
+ };
28235
+ }
28236
+
28237
+ function readPidFile(pidPath) {
28238
+ try {
28239
+ const pid = Number((0,external_node_fs_namespaceObject.readFileSync)(pidPath, 'utf8').trim());
28240
+ return Number.isFinite(pid) && pid > 0 ? pid : null;
28241
+ } catch {
28242
+ return null;
28243
+ }
28244
+ }
28245
+
28246
+ function writePidFile(pidPath, pid) {
28247
+ (0,external_node_fs_namespaceObject.mkdirSync)((0,external_node_path_namespaceObject.dirname)(pidPath), { recursive: true });
28248
+ (0,external_node_fs_namespaceObject.writeFileSync)(pidPath, `${pid}\n`, 'utf8');
28249
+ }
28250
+
28251
+ function removePidFile(pidPath) {
28252
+ (0,external_node_fs_namespaceObject.rmSync)(pidPath, { force: true });
28253
+ }
28254
+
28255
+ function remosidian_isProcessAlive(pid) {
28256
+ try {
28257
+ process.kill(pid, 0);
28258
+ return true;
28259
+ } catch {
28260
+ return false;
28261
+ }
28262
+ }
28263
+
28264
+ function currentEntrypointPath() {
28265
+ const entryPath = (0,external_node_url_namespaceObject.fileURLToPath)(import.meta.url);
28266
+ try {
28267
+ return (0,external_node_fs_namespaceObject.realpathSync)(entryPath);
28268
+ } catch {
28269
+ return entryPath;
28270
+ }
28271
+ }
28272
+
28273
+ function installRuntime(paths) {
28274
+ const runtime = createRuntimePaths(paths);
28275
+ (0,external_node_fs_namespaceObject.mkdirSync)((0,external_node_path_namespaceObject.dirname)(runtime.runtimeEntryPath), { recursive: true });
28276
+ (0,external_node_fs_namespaceObject.copyFileSync)(currentEntrypointPath(), runtime.runtimeEntryPath);
28277
+ (0,external_node_fs_namespaceObject.chmodSync)(runtime.runtimeEntryPath, 0o755);
28278
+ return runtime;
28279
+ }
28280
+
28281
+ function resolveRequestedPort(flags, paths) {
28282
+ return Number(flags.port) || (loadConfigOrNull(paths)?.server?.port ?? 41234);
28283
+ }
28284
+
28214
28285
  function readCsvList(value) {
28215
28286
  if (typeof value !== 'string') return [];
28216
28287
  return value
@@ -28341,7 +28412,7 @@ async function resolveRelayForStart(flags, paths, port) {
28341
28412
 
28342
28413
  function renderQrTerminal(payload) {
28343
28414
  return new Promise((resolveTerm, rejectTerm) => {
28344
- lib.toString(payload, { type: 'terminal', small: true }, (err, str) => {
28415
+ lib.toString(payload, { type: 'terminal', small: true, errorCorrectionLevel: 'M' }, (err, str) => {
28345
28416
  if (err) {
28346
28417
  rejectTerm(err);
28347
28418
  return;
@@ -28353,7 +28424,7 @@ function renderQrTerminal(payload) {
28353
28424
 
28354
28425
  async function writeQrPng(payload, filePath) {
28355
28426
  (0,external_node_fs_namespaceObject.mkdirSync)((0,external_node_path_namespaceObject.dirname)(filePath), { recursive: true });
28356
- await lib.toFile(filePath, payload, { type: 'png', margin: 1, width: 512 });
28427
+ await lib.toFile(filePath, payload, { type: 'png', errorCorrectionLevel: 'M', margin: 1, scale: 8 });
28357
28428
  }
28358
28429
 
28359
28430
  function printHumanHealth(health) {
@@ -28415,7 +28486,214 @@ function buildPairUri(payload) {
28415
28486
  return `remosidian://pair?v=1&data=${encoded}`;
28416
28487
  }
28417
28488
 
28418
- async function cmdStart(flags, paths) {
28489
+ function normalizePairingConnections(payload) {
28490
+ const explicitConnections = Array.isArray(payload.connections)
28491
+ ? payload.connections
28492
+ .filter(
28493
+ (target) =>
28494
+ target &&
28495
+ typeof target.host === 'string' &&
28496
+ Number.isFinite(target.port) &&
28497
+ typeof target.source === 'string' &&
28498
+ typeof target.stability === 'string',
28499
+ )
28500
+ .map((target) => ({
28501
+ kind: target.kind === 'relay' ? 'relay' : 'direct',
28502
+ host: target.host.trim(),
28503
+ port: Math.floor(Number(target.port)),
28504
+ ...(target.secure === true ? { secure: true } : {}),
28505
+ source: target.source,
28506
+ stability: target.stability,
28507
+ }))
28508
+ .filter((target) => target.host && target.port > 0)
28509
+ : [];
28510
+ if (explicitConnections.length > 0) {
28511
+ return explicitConnections;
28512
+ }
28513
+ const fallbackHosts = Array.isArray(payload.fallbackHosts)
28514
+ ? payload.fallbackHosts.filter((host) => typeof host === 'string' && host.trim()).map((host) => host.trim())
28515
+ : [];
28516
+ const port = Number.isFinite(payload.port) && payload.port > 0 ? Math.floor(payload.port) : 41234;
28517
+ return [
28518
+ {
28519
+ kind: 'direct',
28520
+ host: payload.canonicalHost,
28521
+ port,
28522
+ source: 'configured_host',
28523
+ stability: 'stable',
28524
+ },
28525
+ ...fallbackHosts.map((host) => ({
28526
+ kind: 'direct',
28527
+ host,
28528
+ port,
28529
+ source: 'fallback',
28530
+ stability: 'stable',
28531
+ })),
28532
+ ];
28533
+ }
28534
+
28535
+ function createCompactPairingPayload(payload) {
28536
+ const compact = {
28537
+ t: 'rp',
28538
+ d: payload.daemonId,
28539
+ n: payload.displayName,
28540
+ h: payload.canonicalHost,
28541
+ p: payload.port,
28542
+ c: payload.pairNonce,
28543
+ e: payload.expiresAt,
28544
+ };
28545
+ if (Array.isArray(payload.fallbackHosts) && payload.fallbackHosts.length > 0) {
28546
+ compact.f = payload.fallbackHosts;
28547
+ }
28548
+ const connections = normalizePairingConnections(payload);
28549
+ if (connections.length > 0) {
28550
+ compact.o = connections.map((target) => [
28551
+ target.kind,
28552
+ target.host,
28553
+ target.port,
28554
+ target.source,
28555
+ target.stability,
28556
+ target.secure === true,
28557
+ ]);
28558
+ }
28559
+ if (payload.runtime) {
28560
+ compact.r = payload.runtime;
28561
+ }
28562
+ if (Array.isArray(payload.vaults) && payload.vaults.length > 0) {
28563
+ compact.v = payload.vaults;
28564
+ }
28565
+ return compact;
28566
+ }
28567
+
28568
+ function buildPairCode(payload) {
28569
+ return JSON.stringify(createCompactPairingPayload(payload));
28570
+ }
28571
+
28572
+ function buildPairDeepLink(pairingCode) {
28573
+ return `remosidian://pair?data=${encodeURIComponent(pairingCode)}`;
28574
+ }
28575
+
28576
+ function formatConnectionSummary(target) {
28577
+ const scheme = target.secure === true || target.port === 443 ? 'https' : 'http';
28578
+ return `${scheme}://${target.host}:${target.port} (${target.source}, ${target.stability})`;
28579
+ }
28580
+
28581
+ async function printPairingQr(pairingPayload, paths) {
28582
+ const runtime = createRuntimePaths(paths);
28583
+ const pairingCode = buildPairCode(pairingPayload);
28584
+ const deepLink = buildPairDeepLink(pairingCode);
28585
+ const qr = await renderQrTerminal(pairingCode);
28586
+ (0,external_node_fs_namespaceObject.writeFileSync)(runtime.pairingCodePath, `${pairingCode}\n`, 'utf8');
28587
+ await writeQrPng(pairingCode, runtime.pairingQrPath).catch(() => undefined);
28588
+ process.stdout.write(`\nScan this QR code with the Remosidian app:\n\n${qr}\n`);
28589
+ process.stdout.write(`If the terminal QR is hard to scan, open this PNG instead:\n${runtime.pairingQrPath}\n`);
28590
+ process.stdout.write(`If this device cannot scan, paste this pairing code in Remosidian:\n${pairingCode}\n`);
28591
+ process.stdout.write(`Pairing code saved to:\n${runtime.pairingCodePath}\n`);
28592
+ process.stdout.write(`Or open this deep link on a device with Remosidian installed:\n${deepLink}\n`);
28593
+ }
28594
+
28595
+ async function printPairingSummary(port, paths, health = null, flags = {}) {
28596
+ const healthPayload = health ?? (await waitForHealth(port));
28597
+ if (flags.json) {
28598
+ process.stdout.write(`${JSON.stringify({ ok: true, ...healthPayload })}\n`);
28599
+ return;
28600
+ }
28601
+ process.stdout.write(`Remosidian daemon is ready.\n\n`);
28602
+ process.stdout.write(`${printHumanHealth(healthPayload)}\n`);
28603
+ if (flags['no-qr']) {
28604
+ return;
28605
+ }
28606
+ const pairingPayload = await postJson(port, '/pair/open');
28607
+ const connections = normalizePairingConnections(pairingPayload);
28608
+ if (connections.length > 0) {
28609
+ process.stdout.write('\nPairing paths:\n');
28610
+ for (const target of connections) {
28611
+ process.stdout.write(` - ${formatConnectionSummary(target)}\n`);
28612
+ }
28613
+ }
28614
+ await printPairingQr(pairingPayload, paths);
28615
+ }
28616
+
28617
+ async function probeHealth(port, timeoutMs = 0) {
28618
+ const deadline = Date.now() + timeoutMs;
28619
+ do {
28620
+ try {
28621
+ const resp = await fetch(`http://127.0.0.1:${port}/health`, { signal: AbortSignal.timeout(2000) });
28622
+ if (resp.ok) {
28623
+ return await resp.json();
28624
+ }
28625
+ } catch {
28626
+ // Retry below until timeout.
28627
+ }
28628
+ if (timeoutMs <= 0) break;
28629
+ await new Promise((resolveProbe) => setTimeout(resolveProbe, 250));
28630
+ } while (Date.now() < deadline);
28631
+ return null;
28632
+ }
28633
+
28634
+ async function waitForHealth(port, timeoutMs = 30_000) {
28635
+ const health = await probeHealth(port, timeoutMs);
28636
+ if (health) {
28637
+ return health;
28638
+ }
28639
+ throw new Error(`Timed out waiting for Remosidian daemon on 127.0.0.1:${port}`);
28640
+ }
28641
+
28642
+ async function postJson(port, pathname, body = undefined) {
28643
+ const response = await fetch(`http://127.0.0.1:${port}${pathname}`, {
28644
+ method: 'POST',
28645
+ headers: body ? { 'content-type': 'application/json' } : undefined,
28646
+ body: body ? JSON.stringify(body) : undefined,
28647
+ });
28648
+ const payload = await response.json().catch(() => ({}));
28649
+ if (!response.ok) {
28650
+ throw new Error(`${pathname} failed: ${response.status} ${JSON.stringify(payload)}`);
28651
+ }
28652
+ return payload;
28653
+ }
28654
+
28655
+ function buildServeArgs(flags) {
28656
+ const args = ['serve'];
28657
+ for (const vault of flags.__vaults ?? []) {
28658
+ args.push('--vault', vault);
28659
+ }
28660
+ const skipFlags = new Set(['__vaults', 'foreground', 'json']);
28661
+ for (const [key, value] of Object.entries(flags)) {
28662
+ if (skipFlags.has(key)) continue;
28663
+ if (key === 'no-qr') continue;
28664
+ if (value === true) {
28665
+ args.push(`--${key}`);
28666
+ } else if (typeof value === 'string') {
28667
+ args.push(`--${key}`, value);
28668
+ }
28669
+ }
28670
+ args.push('--no-qr');
28671
+ return args;
28672
+ }
28673
+
28674
+ function startDetachedDaemon(paths, flags) {
28675
+ const runtime = installRuntime(paths);
28676
+ (0,external_node_fs_namespaceObject.mkdirSync)(runtime.logDir, { recursive: true });
28677
+ const stdoutFd = (0,external_node_fs_namespaceObject.openSync)(runtime.stdoutLogPath, 'a');
28678
+ const stderrFd = (0,external_node_fs_namespaceObject.openSync)(runtime.stderrLogPath, 'a');
28679
+ try {
28680
+ const child = (0,external_node_child_process_namespaceObject.spawn)(process.execPath, [runtime.runtimeEntryPath, ...buildServeArgs(flags)], {
28681
+ detached: true,
28682
+ stdio: ['ignore', stdoutFd, stderrFd],
28683
+ cwd: process.cwd(),
28684
+ env: { ...process.env },
28685
+ windowsHide: true,
28686
+ });
28687
+ child.unref();
28688
+ writePidFile(runtime.pidPath, child.pid);
28689
+ return child.pid;
28690
+ } finally {
28691
+ (0,external_node_fs_namespaceObject.closeSync)(stdoutFd);
28692
+ (0,external_node_fs_namespaceObject.closeSync)(stderrFd);
28693
+ }
28694
+ }
28695
+
28696
+ async function cmdServe(flags, paths) {
28419
28697
  const config = loadConfigOrNull(paths);
28420
28698
  const inlineVaults = flags.__vaults.map((spec) => expandVaultSpec(spec)).filter((vault) => Boolean(vault));
28421
28699
  let finalConfig;
@@ -28497,16 +28775,7 @@ async function cmdStart(flags, paths) {
28497
28775
  const openResp = await fetch(`http://127.0.0.1:${server.port}/pair/open`, { method: 'POST' });
28498
28776
  if (openResp.ok) {
28499
28777
  const payload = await openResp.json();
28500
- const uri = buildPairUri(payload);
28501
- const qr = await renderQrTerminal(uri);
28502
- process.stdout.write('\nScan to pair (one-time, expires in 5 min):\n');
28503
- process.stdout.write(`${qr}\n`);
28504
- process.stdout.write(`${uri}\n\n`);
28505
- const qrPath = (0,external_node_path_namespaceObject.join)(paths.dataDir, 'pairing-qr.png');
28506
- await writeQrPng(uri, qrPath).catch(() => {
28507
- // PNG export is best-effort.
28508
- });
28509
- process.stdout.write(`Saved QR image: ${qrPath}\n`);
28778
+ await printPairingQr(payload, paths);
28510
28779
  }
28511
28780
  } catch (error) {
28512
28781
  process.stderr.write(`[${PROG}] pair payload not available: ${error?.message ?? error}\n`);
@@ -28529,31 +28798,60 @@ async function cmdStart(flags, paths) {
28529
28798
  process.on('SIGHUP', () => void shutdown('SIGHUP'));
28530
28799
  }
28531
28800
 
28532
- async function cmdPair(flags, paths) {
28533
- const port = Number(flags.port) || (loadConfigOrNull(paths)?.server?.port ?? 41234);
28534
- const openResp = await fetch(`http://127.0.0.1:${port}/pair/open`, { method: 'POST' });
28535
- if (!openResp.ok) {
28536
- process.stderr.write(`[${PROG}] /pair/open failed: ${openResp.status} ${openResp.statusText}\n`);
28537
- process.exit(1);
28801
+ async function cmdStart(flags, paths) {
28802
+ if (flags.foreground) {
28803
+ await cmdServe(flags, paths);
28804
+ return;
28805
+ }
28806
+ const port = resolveRequestedPort(flags, paths);
28807
+ const existing = await probeHealth(port, 1_000);
28808
+ if (existing) {
28809
+ if (!flags.json) {
28810
+ process.stdout.write('Remosidian daemon is already running. Reusing it.\n\n');
28811
+ }
28812
+ await printPairingSummary(port, paths, existing, flags);
28813
+ return;
28814
+ }
28815
+
28816
+ const runtime = createRuntimePaths(paths);
28817
+ const stalePid = readPidFile(runtime.pidPath);
28818
+ if (stalePid && !remosidian_isProcessAlive(stalePid)) {
28819
+ removePidFile(runtime.pidPath);
28820
+ }
28821
+
28822
+ const pid = startDetachedDaemon(paths, flags);
28823
+ const health = await waitForHealth(port);
28824
+ if (!flags.json) {
28825
+ process.stdout.write(`Started Remosidian daemon in the background (pid ${pid}).\n`);
28826
+ process.stdout.write(`Logs: ${runtime.stdoutLogPath} / ${runtime.stderrLogPath}\n\n`);
28538
28827
  }
28539
- const payload = await openResp.json();
28540
- const uri = buildPairUri(payload);
28828
+ await printPairingSummary(port, paths, health, flags);
28829
+ }
28830
+
28831
+ async function cmdPair(flags, paths) {
28832
+ const port = resolveRequestedPort(flags, paths);
28833
+ const payload = await postJson(port, '/pair/open');
28834
+ const pairingCode = buildPairCode(payload);
28835
+ const deepLink = buildPairDeepLink(pairingCode);
28541
28836
  if (flags.json) {
28542
- process.stdout.write(`${JSON.stringify({ ok: true, payload, uri })}\n`);
28837
+ process.stdout.write(`${JSON.stringify({ ok: true, payload, pairingCode, deepLink })}\n`);
28543
28838
  return;
28544
28839
  }
28545
- const qr = await renderQrTerminal(uri);
28840
+ const qr = await renderQrTerminal(pairingCode);
28546
28841
  process.stdout.write(`${qr}\n`);
28547
- process.stdout.write(`${uri}\n`);
28842
+ process.stdout.write(`${pairingCode}\n`);
28843
+ process.stdout.write(`${deepLink}\n`);
28548
28844
  process.stdout.write(`Daemon ${payload.daemonId} (${payload.displayName})\n`);
28549
28845
  process.stdout.write(`Expires ${payload.expiresAt}\n`);
28550
- const qrPath = (0,external_node_path_namespaceObject.join)(paths.dataDir, 'pairing-qr.png');
28551
- await writeQrPng(uri, qrPath).catch(() => undefined);
28552
- process.stdout.write(`Saved QR ${qrPath}\n`);
28846
+ const runtime = createRuntimePaths(paths);
28847
+ (0,external_node_fs_namespaceObject.writeFileSync)(runtime.pairingCodePath, `${pairingCode}\n`, 'utf8');
28848
+ await writeQrPng(pairingCode, runtime.pairingQrPath).catch(() => undefined);
28849
+ process.stdout.write(`Saved QR ${runtime.pairingQrPath}\n`);
28850
+ process.stdout.write(`Saved code ${runtime.pairingCodePath}\n`);
28553
28851
  }
28554
28852
 
28555
28853
  async function cmdStatus(flags, paths) {
28556
- const port = Number(flags.port) || (loadConfigOrNull(paths)?.server?.port ?? 41234);
28854
+ const port = resolveRequestedPort(flags, paths);
28557
28855
  try {
28558
28856
  const resp = await fetch(`http://127.0.0.1:${port}/health`);
28559
28857
  if (!resp.ok) {
@@ -28572,13 +28870,49 @@ async function cmdStatus(flags, paths) {
28572
28870
  }
28573
28871
  }
28574
28872
 
28575
- async function cmdResetPairings(flags, paths) {
28576
- const port = Number(flags.port) || (loadConfigOrNull(paths)?.server?.port ?? 41234);
28577
- const resp = await fetch(`http://127.0.0.1:${port}/pair/reset`, { method: 'POST' });
28578
- if (!resp.ok) {
28579
- process.stderr.write(`[${PROG}] /pair/reset failed: ${resp.status} ${resp.statusText}\n`);
28873
+ async function waitForDaemonExit(port, timeoutMs = 5_000) {
28874
+ const deadline = Date.now() + timeoutMs;
28875
+ while (Date.now() < deadline) {
28876
+ const health = await probeHealth(port, 0);
28877
+ if (!health) {
28878
+ return true;
28879
+ }
28880
+ await new Promise((resolveWait) => setTimeout(resolveWait, 200));
28881
+ }
28882
+ return false;
28883
+ }
28884
+
28885
+ async function cmdStop(flags, paths) {
28886
+ const port = resolveRequestedPort(flags, paths);
28887
+ const runtime = createRuntimePaths(paths);
28888
+ const pid = readPidFile(runtime.pidPath);
28889
+ if (pid && remosidian_isProcessAlive(pid)) {
28890
+ try {
28891
+ process.kill(pid, 'SIGTERM');
28892
+ } catch {
28893
+ // Process may have exited between the liveness check and kill.
28894
+ }
28895
+ await waitForDaemonExit(port);
28896
+ removePidFile(runtime.pidPath);
28897
+ stopTunnel({ tunnelStatePath: (0,external_node_path_namespaceObject.join)(paths.dataDir, 'tunnel.json') });
28898
+ process.stdout.write('Remosidian daemon stopped.\n');
28899
+ return;
28900
+ }
28901
+ removePidFile(runtime.pidPath);
28902
+ const health = await probeHealth(port, 500);
28903
+ if (health) {
28904
+ process.stderr.write(
28905
+ `[${PROG}] daemon is responding on 127.0.0.1:${port}, but no background PID file exists. Stop the foreground terminal process that started it.\n`,
28906
+ );
28580
28907
  process.exit(1);
28581
28908
  }
28909
+ stopTunnel({ tunnelStatePath: (0,external_node_path_namespaceObject.join)(paths.dataDir, 'tunnel.json') });
28910
+ process.stdout.write('Remosidian daemon is not running.\n');
28911
+ }
28912
+
28913
+ async function cmdResetPairings(flags, paths) {
28914
+ const port = resolveRequestedPort(flags, paths);
28915
+ await postJson(port, '/pair/reset');
28582
28916
  process.stdout.write('All pairings revoked.\n');
28583
28917
  }
28584
28918
 
@@ -28652,12 +28986,18 @@ async function main() {
28652
28986
  case 'start':
28653
28987
  await cmdStart(parsed.flags, paths);
28654
28988
  return;
28989
+ case 'serve':
28990
+ await cmdServe(parsed.flags, paths);
28991
+ return;
28655
28992
  case 'pair':
28656
28993
  await cmdPair(parsed.flags, paths);
28657
28994
  return;
28658
28995
  case 'status':
28659
28996
  await cmdStatus(parsed.flags, paths);
28660
28997
  return;
28998
+ case 'stop':
28999
+ await cmdStop(parsed.flags, paths);
29000
+ return;
28661
29001
  case 'reset-pairings':
28662
29002
  await cmdResetPairings(parsed.flags, paths);
28663
29003
  return;
@@ -28681,10 +29021,14 @@ if (entryHref && moduleHref === entryHref) {
28681
29021
 
28682
29022
 
28683
29023
 
29024
+ var __webpack_exports__buildPairCode = __webpack_exports__.gu;
29025
+ var __webpack_exports__buildPairDeepLink = __webpack_exports__.ti;
28684
29026
  var __webpack_exports__buildPairUri = __webpack_exports__.lz;
29027
+ var __webpack_exports__buildServeArgs = __webpack_exports__.jO;
29028
+ var __webpack_exports__createCompactPairingPayload = __webpack_exports__.YP;
28685
29029
  var __webpack_exports__expandVaultSpec = __webpack_exports__.wX;
28686
29030
  var __webpack_exports__main = __webpack_exports__.iW;
28687
29031
  var __webpack_exports__parseArgs = __webpack_exports__.Zy;
28688
29032
  var __webpack_exports__resolveEntrypointHref = __webpack_exports__.Mf;
28689
29033
  var __webpack_exports__resolveInvocation = __webpack_exports__.t0;
28690
- export { __webpack_exports__buildPairUri as buildPairUri, __webpack_exports__expandVaultSpec as expandVaultSpec, __webpack_exports__main as main, __webpack_exports__parseArgs as parseArgs, __webpack_exports__resolveEntrypointHref as resolveEntrypointHref, __webpack_exports__resolveInvocation as resolveInvocation };
29034
+ export { __webpack_exports__buildPairCode as buildPairCode, __webpack_exports__buildPairDeepLink as buildPairDeepLink, __webpack_exports__buildPairUri as buildPairUri, __webpack_exports__buildServeArgs as buildServeArgs, __webpack_exports__createCompactPairingPayload as createCompactPairingPayload, __webpack_exports__expandVaultSpec as expandVaultSpec, __webpack_exports__main as main, __webpack_exports__parseArgs as parseArgs, __webpack_exports__resolveEntrypointHref as resolveEntrypointHref, __webpack_exports__resolveInvocation as resolveInvocation };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "remosidian",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "Run Remosidian — remote, read/write access to your Obsidian vault from any device",
5
5
  "license": "MIT",
6
6
  "homepage": "https://lifeos.vip",