remosidian 0.1.12 → 0.1.13
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 +4 -0
- package/README.md +18 -5
- package/dist/remosidian.mjs +1200 -833
- package/dist/remoteDirect/vibelet-remote-direct-darwin-amd64 +0 -0
- package/dist/remoteDirect/vibelet-remote-direct-darwin-arm64 +0 -0
- package/dist/remoteDirect/vibelet-remote-direct-linux-amd64 +0 -0
- package/dist/remoteDirect/vibelet-remote-direct-linux-arm64 +0 -0
- package/dist/remoteDirect/vibelet-remote-direct-win32-amd64.exe +0 -0
- package/dist/remoteDirect/vibelet-remote-direct-win32-arm64.exe +0 -0
- package/package.json +3 -2
package/dist/remosidian.mjs
CHANGED
|
@@ -15244,21 +15244,22 @@ var __webpack_exports__ = {};
|
|
|
15244
15244
|
|
|
15245
15245
|
// EXPORTS
|
|
15246
15246
|
__nccwpck_require__.d(__webpack_exports__, {
|
|
15247
|
-
gu: () => (/*
|
|
15248
|
-
ti: () => (/*
|
|
15249
|
-
lz: () => (/*
|
|
15250
|
-
jO: () => (/*
|
|
15251
|
-
YP: () => (/*
|
|
15247
|
+
gu: () => (/* reexport */ buildPairCode),
|
|
15248
|
+
ti: () => (/* reexport */ buildPairDeepLink),
|
|
15249
|
+
lz: () => (/* reexport */ buildPairUri),
|
|
15250
|
+
jO: () => (/* reexport */ buildServeArgs),
|
|
15251
|
+
YP: () => (/* reexport */ createCompactPairingPayload),
|
|
15252
15252
|
wX: () => (/* binding */ expandVaultSpec),
|
|
15253
|
-
NT: () => (/*
|
|
15254
|
-
BK: () => (/*
|
|
15253
|
+
NT: () => (/* reexport */ hasLegacyAccessArgs),
|
|
15254
|
+
BK: () => (/* reexport */ helpText),
|
|
15255
15255
|
iW: () => (/* binding */ main),
|
|
15256
|
-
RG: () => (/*
|
|
15257
|
-
QN: () => (/*
|
|
15258
|
-
bo: () => (/*
|
|
15259
|
-
Zy: () => (/*
|
|
15256
|
+
RG: () => (/* reexport */ normalizeCliFlags),
|
|
15257
|
+
QN: () => (/* reexport */ normalizePortValue),
|
|
15258
|
+
bo: () => (/* reexport */ parseAccessValue),
|
|
15259
|
+
Zy: () => (/* reexport */ parseArgs),
|
|
15260
15260
|
Mf: () => (/* binding */ resolveEntrypointHref),
|
|
15261
|
-
t0: () => (/*
|
|
15261
|
+
t0: () => (/* reexport */ resolveInvocation),
|
|
15262
|
+
kr: () => (/* reexport */ resolvePersonalAgentOverride)
|
|
15262
15263
|
});
|
|
15263
15264
|
|
|
15264
15265
|
;// CONCATENATED MODULE: external "node:child_process"
|
|
@@ -15275,6 +15276,203 @@ const promises_namespaceObject = __WEBPACK_EXTERNAL_createRequire(import.meta.ur
|
|
|
15275
15276
|
const external_node_url_namespaceObject = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:url");
|
|
15276
15277
|
// EXTERNAL MODULE: ../../node_modules/qrcode/lib/index.js
|
|
15277
15278
|
var lib = __nccwpck_require__(7514);
|
|
15279
|
+
;// CONCATENATED MODULE: ./bin/cli-pairing.mjs
|
|
15280
|
+
|
|
15281
|
+
|
|
15282
|
+
|
|
15283
|
+
|
|
15284
|
+
function cli_pairing_renderQrTerminal(payload) {
|
|
15285
|
+
return new Promise((resolveTerm, rejectTerm) => {
|
|
15286
|
+
lib.toString(payload, { type: 'terminal', small: true, errorCorrectionLevel: 'M' }, (err, str) => {
|
|
15287
|
+
if (err) {
|
|
15288
|
+
rejectTerm(err);
|
|
15289
|
+
return;
|
|
15290
|
+
}
|
|
15291
|
+
resolveTerm(str);
|
|
15292
|
+
});
|
|
15293
|
+
});
|
|
15294
|
+
}
|
|
15295
|
+
|
|
15296
|
+
async function cli_pairing_writeQrPng(payload, filePath) {
|
|
15297
|
+
(0,external_node_fs_namespaceObject.mkdirSync)((0,external_node_path_namespaceObject.dirname)(filePath), { recursive: true });
|
|
15298
|
+
await lib.toFile(filePath, payload, { type: 'png', errorCorrectionLevel: 'M', margin: 1, scale: 8 });
|
|
15299
|
+
}
|
|
15300
|
+
|
|
15301
|
+
function printHumanHealth(health) {
|
|
15302
|
+
const lines = [];
|
|
15303
|
+
lines.push(`Daemon ${health.daemonId} (${health.displayName})`);
|
|
15304
|
+
lines.push(`Version ${health.version}`);
|
|
15305
|
+
if (health.runtime) {
|
|
15306
|
+
lines.push(`Runtime ${health.runtime === 'cli' ? 'CLI' : 'Obsidian plugin'}`);
|
|
15307
|
+
}
|
|
15308
|
+
lines.push(`Listening http://${health.canonicalHost}:${health.port}`);
|
|
15309
|
+
if (health.relayUrl) {
|
|
15310
|
+
lines.push(`Relay ${health.relayUrl}`);
|
|
15311
|
+
}
|
|
15312
|
+
if (Array.isArray(health.fallbackHosts) && health.fallbackHosts.length > 0) {
|
|
15313
|
+
lines.push(`Fallbacks ${health.fallbackHosts.join(', ')}`);
|
|
15314
|
+
}
|
|
15315
|
+
if (Array.isArray(health.connections) && health.connections.length > 0) {
|
|
15316
|
+
lines.push('Connections:');
|
|
15317
|
+
for (const conn of health.connections) {
|
|
15318
|
+
const proto = conn.secure ? 'wss' : 'ws';
|
|
15319
|
+
lines.push(
|
|
15320
|
+
conn.source === 'remoteDirect'
|
|
15321
|
+
? ' - Remote Direct (stable)'
|
|
15322
|
+
: ` - ${proto}://${conn.host}:${conn.port} (${conn.source}, ${conn.stability})`,
|
|
15323
|
+
);
|
|
15324
|
+
}
|
|
15325
|
+
}
|
|
15326
|
+
if (Array.isArray(health.vaults) && health.vaults.length > 0) {
|
|
15327
|
+
lines.push('Vaults:');
|
|
15328
|
+
for (const vault of health.vaults) {
|
|
15329
|
+
lines.push(` - ${vault.id}: ${vault.root ?? '(unknown root)'}`);
|
|
15330
|
+
}
|
|
15331
|
+
} else {
|
|
15332
|
+
lines.push('Vaults: (none configured — pass --vault or run `remosidian init`)');
|
|
15333
|
+
}
|
|
15334
|
+
lines.push(`Paired ${health.pairedDevices ?? 0} device(s)`);
|
|
15335
|
+
lines.push(`Personal Agent ${health.features?.personalAgent === true ? 'shown' : 'hidden'}`);
|
|
15336
|
+
if (health.ai?.configured) {
|
|
15337
|
+
lines.push(`AI ${health.ai.agent ?? 'agent'} (local CLI)`);
|
|
15338
|
+
} else {
|
|
15339
|
+
lines.push('AI (no claude / codex CLI on PATH — install one to enable Ask)');
|
|
15340
|
+
}
|
|
15341
|
+
return lines.join('\n');
|
|
15342
|
+
}
|
|
15343
|
+
|
|
15344
|
+
function buildPairUri(payload) {
|
|
15345
|
+
// Emit the full schema so @lifeos/client-core's default parser picks it
|
|
15346
|
+
// up. We chose JSON-over-base64url because it survives clipboard paste,
|
|
15347
|
+
// deep links, and QR data URIs without escaping headaches.
|
|
15348
|
+
const full = {
|
|
15349
|
+
type: payload.type,
|
|
15350
|
+
daemonId: payload.daemonId,
|
|
15351
|
+
displayName: payload.displayName,
|
|
15352
|
+
canonicalHost: payload.canonicalHost,
|
|
15353
|
+
port: payload.port,
|
|
15354
|
+
...(payload.fallbackHosts && payload.fallbackHosts.length > 0 ? { fallbackHosts: payload.fallbackHosts } : {}),
|
|
15355
|
+
...(payload.connections && payload.connections.length > 0 ? { connections: payload.connections } : {}),
|
|
15356
|
+
pairNonce: payload.pairNonce,
|
|
15357
|
+
expiresAt: payload.expiresAt,
|
|
15358
|
+
...(payload.runtime ? { runtime: payload.runtime } : {}),
|
|
15359
|
+
...(payload.vaults && payload.vaults.length > 0 ? { vaults: payload.vaults } : {}),
|
|
15360
|
+
};
|
|
15361
|
+
const encoded = Buffer.from(JSON.stringify(full), 'utf8').toString('base64url');
|
|
15362
|
+
return `remosidian://pair?v=1&data=${encoded}`;
|
|
15363
|
+
}
|
|
15364
|
+
|
|
15365
|
+
function normalizePairingConnections(payload) {
|
|
15366
|
+
const explicitConnections = Array.isArray(payload.connections)
|
|
15367
|
+
? payload.connections
|
|
15368
|
+
.filter(
|
|
15369
|
+
(target) =>
|
|
15370
|
+
target &&
|
|
15371
|
+
typeof target.host === 'string' &&
|
|
15372
|
+
Number.isFinite(target.port) &&
|
|
15373
|
+
typeof target.source === 'string' &&
|
|
15374
|
+
typeof target.stability === 'string',
|
|
15375
|
+
)
|
|
15376
|
+
.map((target) => ({
|
|
15377
|
+
kind: target.kind === 'relay' ? 'relay' : 'direct',
|
|
15378
|
+
host: target.host.trim(),
|
|
15379
|
+
port: Math.floor(Number(target.port)),
|
|
15380
|
+
...(target.secure === true ? { secure: true } : {}),
|
|
15381
|
+
...(target.remoteDirect ? { remoteDirect: target.remoteDirect } : {}),
|
|
15382
|
+
source: target.source,
|
|
15383
|
+
stability: target.stability,
|
|
15384
|
+
}))
|
|
15385
|
+
.filter((target) => target.host && target.port > 0)
|
|
15386
|
+
: [];
|
|
15387
|
+
if (explicitConnections.length > 0) {
|
|
15388
|
+
return explicitConnections;
|
|
15389
|
+
}
|
|
15390
|
+
const fallbackHosts = Array.isArray(payload.fallbackHosts)
|
|
15391
|
+
? payload.fallbackHosts.filter((host) => typeof host === 'string' && host.trim()).map((host) => host.trim())
|
|
15392
|
+
: [];
|
|
15393
|
+
const port = Number.isFinite(payload.port) && payload.port > 0 ? Math.floor(payload.port) : 41234;
|
|
15394
|
+
return [
|
|
15395
|
+
{
|
|
15396
|
+
kind: 'direct',
|
|
15397
|
+
host: payload.canonicalHost,
|
|
15398
|
+
port,
|
|
15399
|
+
source: 'configured_host',
|
|
15400
|
+
stability: 'stable',
|
|
15401
|
+
},
|
|
15402
|
+
...fallbackHosts.map((host) => ({
|
|
15403
|
+
kind: 'direct',
|
|
15404
|
+
host,
|
|
15405
|
+
port,
|
|
15406
|
+
source: 'fallback',
|
|
15407
|
+
stability: 'stable',
|
|
15408
|
+
})),
|
|
15409
|
+
];
|
|
15410
|
+
}
|
|
15411
|
+
|
|
15412
|
+
function createCompactPairingPayload(payload) {
|
|
15413
|
+
const compact = {
|
|
15414
|
+
t: 'rp',
|
|
15415
|
+
d: payload.daemonId,
|
|
15416
|
+
n: payload.displayName,
|
|
15417
|
+
h: payload.canonicalHost,
|
|
15418
|
+
p: payload.port,
|
|
15419
|
+
c: payload.pairNonce,
|
|
15420
|
+
e: payload.expiresAt,
|
|
15421
|
+
};
|
|
15422
|
+
if (Array.isArray(payload.fallbackHosts) && payload.fallbackHosts.length > 0) {
|
|
15423
|
+
compact.f = payload.fallbackHosts;
|
|
15424
|
+
}
|
|
15425
|
+
const connections = normalizePairingConnections(payload);
|
|
15426
|
+
if (connections.length > 0) {
|
|
15427
|
+
compact.o = connections.map((target) => [
|
|
15428
|
+
target.kind,
|
|
15429
|
+
target.host,
|
|
15430
|
+
target.port,
|
|
15431
|
+
target.source,
|
|
15432
|
+
target.stability,
|
|
15433
|
+
target.secure === true,
|
|
15434
|
+
...(target.remoteDirect ? [target.remoteDirect.serverAddress, target.remoteDirect.remotePort] : []),
|
|
15435
|
+
]);
|
|
15436
|
+
}
|
|
15437
|
+
if (payload.runtime) {
|
|
15438
|
+
compact.r = payload.runtime;
|
|
15439
|
+
}
|
|
15440
|
+
if (Array.isArray(payload.vaults) && payload.vaults.length > 0) {
|
|
15441
|
+
compact.v = payload.vaults;
|
|
15442
|
+
}
|
|
15443
|
+
return compact;
|
|
15444
|
+
}
|
|
15445
|
+
|
|
15446
|
+
function buildPairCode(payload) {
|
|
15447
|
+
return JSON.stringify(createCompactPairingPayload(payload));
|
|
15448
|
+
}
|
|
15449
|
+
|
|
15450
|
+
function buildPairDeepLink(pairingCode) {
|
|
15451
|
+
return `remosidian://pair?data=${encodeURIComponent(pairingCode)}`;
|
|
15452
|
+
}
|
|
15453
|
+
|
|
15454
|
+
function formatConnectionSummary(target) {
|
|
15455
|
+
if (target.source === 'remoteDirect') return 'Remote Direct (stable)';
|
|
15456
|
+
const scheme = target.secure === true || target.port === 443 ? 'https' : 'http';
|
|
15457
|
+
return `${scheme}://${target.host}:${target.port} (${target.source}, ${target.stability})`;
|
|
15458
|
+
}
|
|
15459
|
+
|
|
15460
|
+
async function printPairingQr(pairingPayload, paths) {
|
|
15461
|
+
const runtime = paths;
|
|
15462
|
+
const pairingCode = buildPairCode(pairingPayload);
|
|
15463
|
+
const deepLink = buildPairDeepLink(pairingCode);
|
|
15464
|
+
const qr = await cli_pairing_renderQrTerminal(pairingCode);
|
|
15465
|
+
(0,external_node_fs_namespaceObject.writeFileSync)(runtime.pairingCodePath, `${pairingCode}\n`, 'utf8');
|
|
15466
|
+
await cli_pairing_writeQrPng(pairingCode, runtime.pairingQrPath).catch(() => undefined);
|
|
15467
|
+
process.stdout.write(`\nScan this QR code with the Remosidian app:\n\n${qr}\n`);
|
|
15468
|
+
process.stdout.write(`If the terminal QR is hard to scan, open this PNG instead:\n${runtime.pairingQrPath}\n`);
|
|
15469
|
+
process.stdout.write(`If this device cannot scan, paste this pairing code in Remosidian:\n${pairingCode}\n`);
|
|
15470
|
+
process.stdout.write(`Pairing code saved to:\n${runtime.pairingCodePath}\n`);
|
|
15471
|
+
process.stdout.write(`Or open this deep link on a device with Remosidian installed:\n${deepLink}\n`);
|
|
15472
|
+
}
|
|
15473
|
+
|
|
15474
|
+
|
|
15475
|
+
|
|
15278
15476
|
;// CONCATENATED MODULE: ../../packages/remosidian-server/dist/paths.js
|
|
15279
15477
|
|
|
15280
15478
|
|
|
@@ -15338,6 +15536,18 @@ function isTruthyEnv(value) {
|
|
|
15338
15536
|
const normalized = value.trim().toLowerCase();
|
|
15339
15537
|
return normalized === '' || normalized === '1' || normalized === 'true' || normalized === 'yes';
|
|
15340
15538
|
}
|
|
15539
|
+
function readBoolean(value, fallback = false) {
|
|
15540
|
+
if (typeof value === 'boolean')
|
|
15541
|
+
return value;
|
|
15542
|
+
if (typeof value !== 'string')
|
|
15543
|
+
return fallback;
|
|
15544
|
+
const normalized = value.trim().toLowerCase();
|
|
15545
|
+
if (['1', 'true', 'yes', 'on'].includes(normalized))
|
|
15546
|
+
return true;
|
|
15547
|
+
if (['0', 'false', 'no', 'off'].includes(normalized))
|
|
15548
|
+
return false;
|
|
15549
|
+
return fallback;
|
|
15550
|
+
}
|
|
15341
15551
|
function getNonBlankPath(value) {
|
|
15342
15552
|
return typeof value === 'string' && value.trim().length > 0 ? value : undefined;
|
|
15343
15553
|
}
|
|
@@ -15426,6 +15636,8 @@ function normalizeRemosidianConfig(value) {
|
|
|
15426
15636
|
: typeof server.relayUrl === 'string'
|
|
15427
15637
|
? server.relayUrl.trim()
|
|
15428
15638
|
: '';
|
|
15639
|
+
const rawFeatures = server.features && typeof server.features === 'object' ? server.features : {};
|
|
15640
|
+
const personalAgent = readBoolean(process.env.REMOSIDIAN_PERSONAL_AGENT, readBoolean(rawFeatures.personalAgent));
|
|
15429
15641
|
const vaults = Array.isArray(raw.vaults)
|
|
15430
15642
|
? raw.vaults.map(normalizeVault).filter((vault) => Boolean(vault))
|
|
15431
15643
|
: [];
|
|
@@ -15436,6 +15648,7 @@ function normalizeRemosidianConfig(value) {
|
|
|
15436
15648
|
port: readPort(envPort ?? server.port, readPort(server.port, DEFAULT_REMOSIDIAN_PORT)),
|
|
15437
15649
|
advertisedHosts,
|
|
15438
15650
|
...(relayUrl ? { relayUrl } : {}),
|
|
15651
|
+
features: { personalAgent },
|
|
15439
15652
|
},
|
|
15440
15653
|
...(ai ? { ai } : {}),
|
|
15441
15654
|
};
|
|
@@ -15452,225 +15665,32 @@ function loadRemosidianConfig(configPath = createRemosidianPaths().configPath) {
|
|
|
15452
15665
|
//# sourceMappingURL=config.js.map
|
|
15453
15666
|
;// CONCATENATED MODULE: external "node:http"
|
|
15454
15667
|
const external_node_http_namespaceObject = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:http");
|
|
15455
|
-
;// CONCATENATED MODULE: ../../packages/daemon-core/dist/
|
|
15456
|
-
|
|
15457
|
-
|
|
15458
|
-
|
|
15459
|
-
|
|
15460
|
-
|
|
15461
|
-
|
|
15462
|
-
|
|
15463
|
-
const cfConnectingIp = firstHeaderValue(req.headers["cf-connecting-ip"]);
|
|
15464
|
-
const realIp = firstHeaderValue(req.headers["x-real-ip"]);
|
|
15465
|
-
const ip = forwardedFor?.split(",")[0]?.trim() ||
|
|
15466
|
-
cfConnectingIp ||
|
|
15467
|
-
realIp ||
|
|
15468
|
-
req.socket.remoteAddress ||
|
|
15469
|
-
undefined;
|
|
15470
|
-
const userAgent = firstHeaderValue(req.headers["user-agent"]);
|
|
15471
|
-
return {
|
|
15472
|
-
...(ip ? { ip } : {}),
|
|
15473
|
-
...(userAgent ? { userAgent } : {}),
|
|
15474
|
-
};
|
|
15668
|
+
;// CONCATENATED MODULE: ../../packages/daemon-core/dist/advertised-hosts.js
|
|
15669
|
+
|
|
15670
|
+
|
|
15671
|
+
function isIpv4(address) {
|
|
15672
|
+
const parts = address.split('.');
|
|
15673
|
+
if (parts.length !== 4)
|
|
15674
|
+
return false;
|
|
15675
|
+
return parts.every((part) => /^\d+$/.test(part) && Number(part) >= 0 && Number(part) <= 255);
|
|
15475
15676
|
}
|
|
15476
|
-
function
|
|
15477
|
-
if (!address)
|
|
15677
|
+
function isSharedIpv4(address) {
|
|
15678
|
+
if (!isIpv4(address))
|
|
15478
15679
|
return false;
|
|
15479
|
-
|
|
15480
|
-
|
|
15481
|
-
address === "::ffff:127.0.0.1");
|
|
15680
|
+
const [first, second] = address.split('.').map(Number);
|
|
15681
|
+
return first === 100 && second >= 64 && second <= 127;
|
|
15482
15682
|
}
|
|
15483
|
-
|
|
15484
|
-
|
|
15485
|
-
|
|
15486
|
-
|
|
15487
|
-
|
|
15488
|
-
"forwarded",
|
|
15489
|
-
"via",
|
|
15490
|
-
"cf-connecting-ip",
|
|
15491
|
-
"cf-ray",
|
|
15492
|
-
];
|
|
15493
|
-
function isProxiedRequest(headers) {
|
|
15494
|
-
for (const name of PROXY_HEADER_NAMES) {
|
|
15495
|
-
if (headers[name] !== undefined)
|
|
15496
|
-
return true;
|
|
15497
|
-
}
|
|
15498
|
-
return false;
|
|
15683
|
+
function isLinkLocalIpv4(address) {
|
|
15684
|
+
if (!isIpv4(address))
|
|
15685
|
+
return false;
|
|
15686
|
+
const [first, second] = address.split('.').map(Number);
|
|
15687
|
+
return first === 169 && second === 254;
|
|
15499
15688
|
}
|
|
15500
|
-
function
|
|
15501
|
-
|
|
15502
|
-
|
|
15503
|
-
|
|
15504
|
-
|
|
15505
|
-
if (!header)
|
|
15506
|
-
return null;
|
|
15507
|
-
const match = header.match(/^Bearer\s+(.+)$/i);
|
|
15508
|
-
return match?.[1]?.trim() || null;
|
|
15509
|
-
}
|
|
15510
|
-
function resolveRequestToken(authHeader, queryToken) {
|
|
15511
|
-
return readBearerToken(authHeader) ?? queryToken;
|
|
15512
|
-
}
|
|
15513
|
-
function isLegacyToken(token, legacyToken) {
|
|
15514
|
-
return Boolean(token && legacyToken && token === legacyToken);
|
|
15515
|
-
}
|
|
15516
|
-
function isAuthorizedToken(token, legacyToken, validatePairToken, touch = true) {
|
|
15517
|
-
if (!token)
|
|
15518
|
-
return false;
|
|
15519
|
-
if (isLegacyToken(token, legacyToken))
|
|
15520
|
-
return true;
|
|
15521
|
-
return Boolean(validatePairToken(token, touch));
|
|
15522
|
-
}
|
|
15523
|
-
//# sourceMappingURL=auth.js.map
|
|
15524
|
-
;// CONCATENATED MODULE: external "node:crypto"
|
|
15525
|
-
const external_node_crypto_namespaceObject = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:crypto");
|
|
15526
|
-
;// CONCATENATED MODULE: ../../packages/daemon-core/dist/identity.js
|
|
15527
|
-
|
|
15528
|
-
|
|
15529
|
-
|
|
15530
|
-
|
|
15531
|
-
|
|
15532
|
-
function base64Url(bytes) {
|
|
15533
|
-
return (0,external_node_crypto_namespaceObject.randomBytes)(bytes).toString('base64url');
|
|
15534
|
-
}
|
|
15535
|
-
function buildDaemonId() {
|
|
15536
|
-
return `d_${base64Url(12)}`;
|
|
15537
|
-
}
|
|
15538
|
-
function defaultDisplayName(options) {
|
|
15539
|
-
return options?.systemHostname ?? (0,external_node_os_namespaceObject.hostname)();
|
|
15540
|
-
}
|
|
15541
|
-
function runHostnameCommand(command, args) {
|
|
15542
|
-
return (0,external_node_child_process_namespaceObject.execFileSync)(command, args, {
|
|
15543
|
-
encoding: 'utf8',
|
|
15544
|
-
stdio: ['ignore', 'pipe', 'ignore'],
|
|
15545
|
-
timeout: 1_000,
|
|
15546
|
-
});
|
|
15547
|
-
}
|
|
15548
|
-
function normalizeMdnsHostname(value) {
|
|
15549
|
-
const raw = value.trim().replace(/\.$/, '').toLowerCase();
|
|
15550
|
-
if (!raw)
|
|
15551
|
-
return null;
|
|
15552
|
-
const firstLabel = raw.endsWith('.local') ? raw.slice(0, -6) : raw.split('.')[0];
|
|
15553
|
-
const label = firstLabel
|
|
15554
|
-
.normalize('NFKD')
|
|
15555
|
-
.replace(/[^a-z0-9-]+/g, '-')
|
|
15556
|
-
.replace(/^-+|-+$/g, '')
|
|
15557
|
-
.slice(0, 63);
|
|
15558
|
-
return label ? `${label}.local` : null;
|
|
15559
|
-
}
|
|
15560
|
-
/**
|
|
15561
|
-
* Discover the platform's real local host label before projecting it into the
|
|
15562
|
-
* mDNS `.local` namespace. macOS has a dedicated Bonjour LocalHostName that
|
|
15563
|
-
* can differ from `os.hostname()` on managed machines. Linux prefers its
|
|
15564
|
-
* configured static hostname; Windows' `os.hostname()` reflects COMPUTERNAME.
|
|
15565
|
-
* Whether another device can resolve the resulting name is verified by the
|
|
15566
|
-
* client after an IP pairing succeeds.
|
|
15567
|
-
*/
|
|
15568
|
-
function discoverPlatformLocalHostname(options = {}) {
|
|
15569
|
-
const platform = options.platform ?? process.platform;
|
|
15570
|
-
const systemHostname = options.systemHostname ?? (0,external_node_os_namespaceObject.hostname)();
|
|
15571
|
-
const runCommand = options.runCommand ?? runHostnameCommand;
|
|
15572
|
-
const command = platform === 'darwin'
|
|
15573
|
-
? ['scutil', ['--get', 'LocalHostName']]
|
|
15574
|
-
: platform === 'linux'
|
|
15575
|
-
? ['hostnamectl', ['--static']]
|
|
15576
|
-
: null;
|
|
15577
|
-
if (command) {
|
|
15578
|
-
try {
|
|
15579
|
-
const discovered = normalizeMdnsHostname(runCommand(command[0], [...command[1]]));
|
|
15580
|
-
if (discovered)
|
|
15581
|
-
return discovered;
|
|
15582
|
-
}
|
|
15583
|
-
catch {
|
|
15584
|
-
// Minimal Linux containers and older macOS installations may not have
|
|
15585
|
-
// the platform helper. `os.hostname()` remains the portable fallback.
|
|
15586
|
-
}
|
|
15587
|
-
}
|
|
15588
|
-
return normalizeMdnsHostname(systemHostname) ?? 'localhost';
|
|
15589
|
-
}
|
|
15590
|
-
function defaultCanonicalHost(options) {
|
|
15591
|
-
return discoverPlatformLocalHostname(options);
|
|
15592
|
-
}
|
|
15593
|
-
function createIdentity(port, hostnameOptions) {
|
|
15594
|
-
return {
|
|
15595
|
-
daemonId: buildDaemonId(),
|
|
15596
|
-
daemonSecret: base64Url(32),
|
|
15597
|
-
displayName: defaultDisplayName(hostnameOptions),
|
|
15598
|
-
canonicalHost: defaultCanonicalHost(hostnameOptions),
|
|
15599
|
-
port,
|
|
15600
|
-
createdAt: new Date().toISOString(),
|
|
15601
|
-
};
|
|
15602
|
-
}
|
|
15603
|
-
function writeIdentity(identity, identityPath) {
|
|
15604
|
-
(0,external_node_fs_namespaceObject.mkdirSync)((0,external_node_path_namespaceObject.dirname)(identityPath), { recursive: true });
|
|
15605
|
-
(0,external_node_fs_namespaceObject.writeFileSync)(identityPath, `${JSON.stringify(identity, null, 2)}\n`, 'utf8');
|
|
15606
|
-
}
|
|
15607
|
-
function loadOrCreateDaemonIdentity(port, identityPath, hostnameOptions) {
|
|
15608
|
-
const discoveredCanonicalHost = defaultCanonicalHost(hostnameOptions);
|
|
15609
|
-
try {
|
|
15610
|
-
const raw = (0,external_node_fs_namespaceObject.readFileSync)(identityPath, 'utf8');
|
|
15611
|
-
const parsed = JSON.parse(raw);
|
|
15612
|
-
if (typeof parsed.daemonId === 'string' &&
|
|
15613
|
-
typeof parsed.daemonSecret === 'string' &&
|
|
15614
|
-
typeof parsed.displayName === 'string' &&
|
|
15615
|
-
typeof parsed.canonicalHost === 'string' &&
|
|
15616
|
-
typeof parsed.createdAt === 'string') {
|
|
15617
|
-
const identity = {
|
|
15618
|
-
daemonId: parsed.daemonId,
|
|
15619
|
-
daemonSecret: parsed.daemonSecret,
|
|
15620
|
-
displayName: parsed.displayName,
|
|
15621
|
-
canonicalHost: parsed.canonicalHost,
|
|
15622
|
-
port: typeof parsed.port === 'number' && Number.isFinite(parsed.port) ? parsed.port : port,
|
|
15623
|
-
createdAt: parsed.createdAt,
|
|
15624
|
-
};
|
|
15625
|
-
const legacyGeneratedHost = normalizeMdnsHostname(parsed.displayName);
|
|
15626
|
-
if (legacyGeneratedHost &&
|
|
15627
|
-
identity.canonicalHost.toLowerCase() === legacyGeneratedHost &&
|
|
15628
|
-
identity.canonicalHost !== discoveredCanonicalHost) {
|
|
15629
|
-
identity.canonicalHost = discoveredCanonicalHost;
|
|
15630
|
-
}
|
|
15631
|
-
if (identity.port !== port) {
|
|
15632
|
-
identity.port = port;
|
|
15633
|
-
}
|
|
15634
|
-
if (identity.port !== parsed.port || identity.canonicalHost !== parsed.canonicalHost) {
|
|
15635
|
-
writeIdentity(identity, identityPath);
|
|
15636
|
-
}
|
|
15637
|
-
return identity;
|
|
15638
|
-
}
|
|
15639
|
-
}
|
|
15640
|
-
catch {
|
|
15641
|
-
// Fall through to create a new identity.
|
|
15642
|
-
}
|
|
15643
|
-
const identity = createIdentity(port, hostnameOptions);
|
|
15644
|
-
writeIdentity(identity, identityPath);
|
|
15645
|
-
return identity;
|
|
15646
|
-
}
|
|
15647
|
-
//# sourceMappingURL=identity.js.map
|
|
15648
|
-
;// CONCATENATED MODULE: ../../packages/daemon-core/dist/advertised-hosts.js
|
|
15649
|
-
|
|
15650
|
-
|
|
15651
|
-
function isIpv4(address) {
|
|
15652
|
-
const parts = address.split('.');
|
|
15653
|
-
if (parts.length !== 4)
|
|
15654
|
-
return false;
|
|
15655
|
-
return parts.every((part) => /^\d+$/.test(part) && Number(part) >= 0 && Number(part) <= 255);
|
|
15656
|
-
}
|
|
15657
|
-
function isSharedIpv4(address) {
|
|
15658
|
-
if (!isIpv4(address))
|
|
15659
|
-
return false;
|
|
15660
|
-
const [first, second] = address.split('.').map(Number);
|
|
15661
|
-
return first === 100 && second >= 64 && second <= 127;
|
|
15662
|
-
}
|
|
15663
|
-
function isLinkLocalIpv4(address) {
|
|
15664
|
-
if (!isIpv4(address))
|
|
15665
|
-
return false;
|
|
15666
|
-
const [first, second] = address.split('.').map(Number);
|
|
15667
|
-
return first === 169 && second === 254;
|
|
15668
|
-
}
|
|
15669
|
-
function isBenchmarkIpv4(address) {
|
|
15670
|
-
if (!isIpv4(address))
|
|
15671
|
-
return false;
|
|
15672
|
-
const [first, second] = address.split('.').map(Number);
|
|
15673
|
-
return first === 198 && (second === 18 || second === 19);
|
|
15689
|
+
function isBenchmarkIpv4(address) {
|
|
15690
|
+
if (!isIpv4(address))
|
|
15691
|
+
return false;
|
|
15692
|
+
const [first, second] = address.split('.').map(Number);
|
|
15693
|
+
return first === 198 && (second === 18 || second === 19);
|
|
15674
15694
|
}
|
|
15675
15695
|
function isPrivateIpv4(address) {
|
|
15676
15696
|
if (!isIpv4(address))
|
|
@@ -15959,139 +15979,199 @@ function computeAdvertisedHosts(options) {
|
|
|
15959
15979
|
};
|
|
15960
15980
|
}
|
|
15961
15981
|
//# sourceMappingURL=advertised-hosts.js.map
|
|
15962
|
-
;// CONCATENATED MODULE: ../../packages/daemon-core/dist/
|
|
15963
|
-
|
|
15964
|
-
|
|
15965
|
-
|
|
15966
|
-
|
|
15967
|
-
return (0,external_node_crypto_namespaceObject.randomBytes)(bytes).toString("base64url");
|
|
15982
|
+
;// CONCATENATED MODULE: ../../packages/daemon-core/dist/auth.js
|
|
15983
|
+
function firstHeaderValue(value) {
|
|
15984
|
+
if (Array.isArray(value))
|
|
15985
|
+
return value[0];
|
|
15986
|
+
return value;
|
|
15968
15987
|
}
|
|
15969
|
-
function
|
|
15970
|
-
|
|
15988
|
+
function extractClientFingerprint(req) {
|
|
15989
|
+
const forwardedFor = firstHeaderValue(req.headers["x-forwarded-for"]);
|
|
15990
|
+
const cfConnectingIp = firstHeaderValue(req.headers["cf-connecting-ip"]);
|
|
15991
|
+
const realIp = firstHeaderValue(req.headers["x-real-ip"]);
|
|
15992
|
+
const ip = forwardedFor?.split(",")[0]?.trim() ||
|
|
15993
|
+
cfConnectingIp ||
|
|
15994
|
+
realIp ||
|
|
15995
|
+
req.socket.remoteAddress ||
|
|
15996
|
+
undefined;
|
|
15997
|
+
const userAgent = firstHeaderValue(req.headers["user-agent"]);
|
|
15998
|
+
return {
|
|
15999
|
+
...(ip ? { ip } : {}),
|
|
16000
|
+
...(userAgent ? { userAgent } : {}),
|
|
16001
|
+
};
|
|
15971
16002
|
}
|
|
15972
|
-
function
|
|
15973
|
-
|
|
15974
|
-
const rightBuffer = Buffer.from(right);
|
|
15975
|
-
if (leftBuffer.length !== rightBuffer.length)
|
|
16003
|
+
function isLoopbackAddress(address) {
|
|
16004
|
+
if (!address)
|
|
15976
16005
|
return false;
|
|
15977
|
-
return (0
|
|
16006
|
+
return (address === "127.0.0.1" ||
|
|
16007
|
+
address === "::1" ||
|
|
16008
|
+
address === "::ffff:127.0.0.1");
|
|
15978
16009
|
}
|
|
15979
|
-
|
|
15980
|
-
|
|
15981
|
-
|
|
15982
|
-
|
|
15983
|
-
|
|
15984
|
-
|
|
15985
|
-
|
|
15986
|
-
|
|
15987
|
-
|
|
15988
|
-
|
|
15989
|
-
|
|
15990
|
-
|
|
15991
|
-
|
|
15992
|
-
|
|
15993
|
-
catch {
|
|
15994
|
-
return [];
|
|
16010
|
+
const PROXY_HEADER_NAMES = [
|
|
16011
|
+
"x-forwarded-for",
|
|
16012
|
+
"x-forwarded-host",
|
|
16013
|
+
"x-forwarded-proto",
|
|
16014
|
+
"x-real-ip",
|
|
16015
|
+
"forwarded",
|
|
16016
|
+
"via",
|
|
16017
|
+
"cf-connecting-ip",
|
|
16018
|
+
"cf-ray",
|
|
16019
|
+
];
|
|
16020
|
+
function isProxiedRequest(headers) {
|
|
16021
|
+
for (const name of PROXY_HEADER_NAMES) {
|
|
16022
|
+
if (headers[name] !== undefined)
|
|
16023
|
+
return true;
|
|
15995
16024
|
}
|
|
16025
|
+
return false;
|
|
15996
16026
|
}
|
|
15997
|
-
function
|
|
15998
|
-
(
|
|
15999
|
-
|
|
16027
|
+
function isTrustedLocalRequest(req) {
|
|
16028
|
+
return (isLoopbackAddress(req.socket.remoteAddress) &&
|
|
16029
|
+
!isProxiedRequest(req.headers));
|
|
16000
16030
|
}
|
|
16001
|
-
|
|
16002
|
-
|
|
16003
|
-
|
|
16004
|
-
|
|
16005
|
-
|
|
16006
|
-
|
|
16007
|
-
|
|
16008
|
-
|
|
16009
|
-
|
|
16010
|
-
|
|
16011
|
-
|
|
16012
|
-
|
|
16013
|
-
|
|
16014
|
-
|
|
16015
|
-
|
|
16016
|
-
|
|
16017
|
-
|
|
16018
|
-
|
|
16019
|
-
|
|
16020
|
-
|
|
16021
|
-
|
|
16022
|
-
|
|
16023
|
-
|
|
16024
|
-
|
|
16025
|
-
|
|
16026
|
-
|
|
16027
|
-
|
|
16028
|
-
|
|
16029
|
-
|
|
16030
|
-
|
|
16031
|
-
|
|
16032
|
-
|
|
16033
|
-
|
|
16034
|
-
|
|
16035
|
-
|
|
16036
|
-
|
|
16037
|
-
|
|
16038
|
-
|
|
16039
|
-
|
|
16040
|
-
|
|
16041
|
-
|
|
16042
|
-
|
|
16043
|
-
|
|
16044
|
-
|
|
16045
|
-
|
|
16046
|
-
|
|
16047
|
-
|
|
16048
|
-
|
|
16049
|
-
|
|
16050
|
-
|
|
16051
|
-
|
|
16031
|
+
function readBearerToken(header) {
|
|
16032
|
+
if (!header)
|
|
16033
|
+
return null;
|
|
16034
|
+
const match = header.match(/^Bearer\s+(.+)$/i);
|
|
16035
|
+
return match?.[1]?.trim() || null;
|
|
16036
|
+
}
|
|
16037
|
+
function resolveRequestToken(authHeader, queryToken) {
|
|
16038
|
+
return readBearerToken(authHeader) ?? queryToken;
|
|
16039
|
+
}
|
|
16040
|
+
function isLegacyToken(token, legacyToken) {
|
|
16041
|
+
return Boolean(token && legacyToken && token === legacyToken);
|
|
16042
|
+
}
|
|
16043
|
+
function isAuthorizedToken(token, legacyToken, validatePairToken, touch = true) {
|
|
16044
|
+
if (!token)
|
|
16045
|
+
return false;
|
|
16046
|
+
if (isLegacyToken(token, legacyToken))
|
|
16047
|
+
return true;
|
|
16048
|
+
return Boolean(validatePairToken(token, touch));
|
|
16049
|
+
}
|
|
16050
|
+
//# sourceMappingURL=auth.js.map
|
|
16051
|
+
;// CONCATENATED MODULE: external "node:crypto"
|
|
16052
|
+
const external_node_crypto_namespaceObject = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:crypto");
|
|
16053
|
+
;// CONCATENATED MODULE: ../../packages/daemon-core/dist/identity.js
|
|
16054
|
+
|
|
16055
|
+
|
|
16056
|
+
|
|
16057
|
+
|
|
16058
|
+
|
|
16059
|
+
function base64Url(bytes) {
|
|
16060
|
+
return (0,external_node_crypto_namespaceObject.randomBytes)(bytes).toString('base64url');
|
|
16061
|
+
}
|
|
16062
|
+
function buildDaemonId() {
|
|
16063
|
+
return `d_${base64Url(12)}`;
|
|
16064
|
+
}
|
|
16065
|
+
function defaultDisplayName(options) {
|
|
16066
|
+
return options?.systemHostname ?? (0,external_node_os_namespaceObject.hostname)();
|
|
16067
|
+
}
|
|
16068
|
+
function runHostnameCommand(command, args) {
|
|
16069
|
+
return (0,external_node_child_process_namespaceObject.execFileSync)(command, args, {
|
|
16070
|
+
encoding: 'utf8',
|
|
16071
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
16072
|
+
timeout: 1_000,
|
|
16073
|
+
});
|
|
16074
|
+
}
|
|
16075
|
+
function normalizeMdnsHostname(value) {
|
|
16076
|
+
const raw = value.trim().replace(/\.$/, '').toLowerCase();
|
|
16077
|
+
if (!raw)
|
|
16078
|
+
return null;
|
|
16079
|
+
const firstLabel = raw.endsWith('.local') ? raw.slice(0, -6) : raw.split('.')[0];
|
|
16080
|
+
const label = firstLabel
|
|
16081
|
+
.normalize('NFKD')
|
|
16082
|
+
.replace(/[^a-z0-9-]+/g, '-')
|
|
16083
|
+
.replace(/^-+|-+$/g, '')
|
|
16084
|
+
.slice(0, 63);
|
|
16085
|
+
return label ? `${label}.local` : null;
|
|
16086
|
+
}
|
|
16087
|
+
/**
|
|
16088
|
+
* Discover the platform's real local host label before projecting it into the
|
|
16089
|
+
* mDNS `.local` namespace. macOS has a dedicated Bonjour LocalHostName that
|
|
16090
|
+
* can differ from `os.hostname()` on managed machines. Linux prefers its
|
|
16091
|
+
* configured static hostname; Windows' `os.hostname()` reflects COMPUTERNAME.
|
|
16092
|
+
* Whether another device can resolve the resulting name is verified by the
|
|
16093
|
+
* client after an IP pairing succeeds.
|
|
16094
|
+
*/
|
|
16095
|
+
function discoverPlatformLocalHostname(options = {}) {
|
|
16096
|
+
const platform = options.platform ?? process.platform;
|
|
16097
|
+
const systemHostname = options.systemHostname ?? (0,external_node_os_namespaceObject.hostname)();
|
|
16098
|
+
const runCommand = options.runCommand ?? runHostnameCommand;
|
|
16099
|
+
const command = platform === 'darwin'
|
|
16100
|
+
? ['scutil', ['--get', 'LocalHostName']]
|
|
16101
|
+
: platform === 'linux'
|
|
16102
|
+
? ['hostnamectl', ['--static']]
|
|
16103
|
+
: null;
|
|
16104
|
+
if (command) {
|
|
16105
|
+
try {
|
|
16106
|
+
const discovered = normalizeMdnsHostname(runCommand(command[0], [...command[1]]));
|
|
16107
|
+
if (discovered)
|
|
16108
|
+
return discovered;
|
|
16052
16109
|
}
|
|
16053
|
-
|
|
16054
|
-
|
|
16055
|
-
|
|
16056
|
-
validatePairToken(deviceId, token, touch = true) {
|
|
16057
|
-
const record = this.records.find((entry) => entry.deviceId === deviceId && !entry.revokedAt);
|
|
16058
|
-
if (!record)
|
|
16059
|
-
return false;
|
|
16060
|
-
const tokenDigest = hashToken(token);
|
|
16061
|
-
if (!sameHash(record.tokenHash, tokenDigest))
|
|
16062
|
-
return false;
|
|
16063
|
-
if (touch) {
|
|
16064
|
-
record.lastSeenAt = new Date().toISOString();
|
|
16065
|
-
writeRecords(this.pairingsPath, this.records);
|
|
16110
|
+
catch {
|
|
16111
|
+
// Minimal Linux containers and older macOS installations may not have
|
|
16112
|
+
// the platform helper. `os.hostname()` remains the portable fallback.
|
|
16066
16113
|
}
|
|
16067
|
-
return true;
|
|
16068
16114
|
}
|
|
16069
|
-
|
|
16070
|
-
|
|
16071
|
-
|
|
16072
|
-
|
|
16073
|
-
|
|
16074
|
-
|
|
16075
|
-
|
|
16076
|
-
|
|
16115
|
+
return normalizeMdnsHostname(systemHostname) ?? 'localhost';
|
|
16116
|
+
}
|
|
16117
|
+
function defaultCanonicalHost(options) {
|
|
16118
|
+
return discoverPlatformLocalHostname(options);
|
|
16119
|
+
}
|
|
16120
|
+
function createIdentity(port, hostnameOptions) {
|
|
16121
|
+
return {
|
|
16122
|
+
daemonId: buildDaemonId(),
|
|
16123
|
+
daemonSecret: base64Url(32),
|
|
16124
|
+
displayName: defaultDisplayName(hostnameOptions),
|
|
16125
|
+
canonicalHost: defaultCanonicalHost(hostnameOptions),
|
|
16126
|
+
port,
|
|
16127
|
+
createdAt: new Date().toISOString(),
|
|
16128
|
+
};
|
|
16129
|
+
}
|
|
16130
|
+
function writeIdentity(identity, identityPath) {
|
|
16131
|
+
(0,external_node_fs_namespaceObject.mkdirSync)((0,external_node_path_namespaceObject.dirname)(identityPath), { recursive: true });
|
|
16132
|
+
(0,external_node_fs_namespaceObject.writeFileSync)(identityPath, `${JSON.stringify(identity, null, 2)}\n`, 'utf8');
|
|
16133
|
+
}
|
|
16134
|
+
function loadOrCreateDaemonIdentity(port, identityPath, hostnameOptions) {
|
|
16135
|
+
const discoveredCanonicalHost = defaultCanonicalHost(hostnameOptions);
|
|
16136
|
+
try {
|
|
16137
|
+
const raw = (0,external_node_fs_namespaceObject.readFileSync)(identityPath, 'utf8');
|
|
16138
|
+
const parsed = JSON.parse(raw);
|
|
16139
|
+
if (typeof parsed.daemonId === 'string' &&
|
|
16140
|
+
typeof parsed.daemonSecret === 'string' &&
|
|
16141
|
+
typeof parsed.displayName === 'string' &&
|
|
16142
|
+
typeof parsed.canonicalHost === 'string' &&
|
|
16143
|
+
typeof parsed.createdAt === 'string') {
|
|
16144
|
+
const identity = {
|
|
16145
|
+
daemonId: parsed.daemonId,
|
|
16146
|
+
daemonSecret: parsed.daemonSecret,
|
|
16147
|
+
displayName: parsed.displayName,
|
|
16148
|
+
canonicalHost: parsed.canonicalHost,
|
|
16149
|
+
port: typeof parsed.port === 'number' && Number.isFinite(parsed.port) ? parsed.port : port,
|
|
16150
|
+
createdAt: parsed.createdAt,
|
|
16151
|
+
};
|
|
16152
|
+
const legacyGeneratedHost = normalizeMdnsHostname(parsed.displayName);
|
|
16153
|
+
if (legacyGeneratedHost &&
|
|
16154
|
+
identity.canonicalHost.toLowerCase() === legacyGeneratedHost &&
|
|
16155
|
+
identity.canonicalHost !== discoveredCanonicalHost) {
|
|
16156
|
+
identity.canonicalHost = discoveredCanonicalHost;
|
|
16157
|
+
}
|
|
16158
|
+
if (identity.port !== port) {
|
|
16159
|
+
identity.port = port;
|
|
16160
|
+
}
|
|
16161
|
+
if (identity.port !== parsed.port || identity.canonicalHost !== parsed.canonicalHost) {
|
|
16162
|
+
writeIdentity(identity, identityPath);
|
|
16163
|
+
}
|
|
16164
|
+
return identity;
|
|
16077
16165
|
}
|
|
16078
|
-
return record;
|
|
16079
|
-
}
|
|
16080
|
-
revoke(deviceId) {
|
|
16081
|
-
const record = this.records.find((entry) => entry.deviceId === deviceId && !entry.revokedAt);
|
|
16082
|
-
if (!record)
|
|
16083
|
-
return false;
|
|
16084
|
-
record.revokedAt = new Date().toISOString();
|
|
16085
|
-
writeRecords(this.pairingsPath, this.records);
|
|
16086
|
-
return true;
|
|
16087
16166
|
}
|
|
16088
|
-
|
|
16089
|
-
|
|
16090
|
-
this.windows.clear();
|
|
16091
|
-
writeRecords(this.pairingsPath, this.records);
|
|
16167
|
+
catch {
|
|
16168
|
+
// Fall through to create a new identity.
|
|
16092
16169
|
}
|
|
16170
|
+
const identity = createIdentity(port, hostnameOptions);
|
|
16171
|
+
writeIdentity(identity, identityPath);
|
|
16172
|
+
return identity;
|
|
16093
16173
|
}
|
|
16094
|
-
//# sourceMappingURL=
|
|
16174
|
+
//# sourceMappingURL=identity.js.map
|
|
16095
16175
|
;// CONCATENATED MODULE: ../../packages/daemon-core/dist/pairing-http.js
|
|
16096
16176
|
// Reusable HTTP pairing handlers for daemon products (Vibelet, Remosidian, ...).
|
|
16097
16177
|
//
|
|
@@ -16225,6 +16305,7 @@ function createPairingHttpHandler(deps) {
|
|
|
16225
16305
|
return;
|
|
16226
16306
|
}
|
|
16227
16307
|
deps.pairings.reset();
|
|
16308
|
+
deps.onReset?.();
|
|
16228
16309
|
sendJson(res, 200, { ok: true });
|
|
16229
16310
|
};
|
|
16230
16311
|
const tryHandle = async (req, res, pathname) => {
|
|
@@ -16247,47 +16328,352 @@ function createPairingHttpHandler(deps) {
|
|
|
16247
16328
|
return { openPairing, handleOpen, handleCreate, handleReset, tryHandle };
|
|
16248
16329
|
}
|
|
16249
16330
|
//# sourceMappingURL=pairing-http.js.map
|
|
16250
|
-
;// CONCATENATED MODULE: ../../packages/daemon-core/dist/
|
|
16251
|
-
|
|
16252
|
-
|
|
16331
|
+
;// CONCATENATED MODULE: ../../packages/daemon-core/dist/pairing-store.js
|
|
16253
16332
|
|
|
16254
16333
|
|
|
16255
16334
|
|
|
16256
|
-
|
|
16257
|
-
|
|
16258
|
-
function isAssistantStatus(value) {
|
|
16259
|
-
if (!value || typeof value !== 'object')
|
|
16260
|
-
return false;
|
|
16261
|
-
const v = value;
|
|
16262
|
-
return (typeof v.enabled === 'boolean' &&
|
|
16263
|
-
typeof v.connected === 'boolean' &&
|
|
16264
|
-
typeof v.paused === 'boolean' &&
|
|
16265
|
-
Number.isFinite(v.checkedAt));
|
|
16335
|
+
function pairing_store_base64Url(bytes) {
|
|
16336
|
+
return (0,external_node_crypto_namespaceObject.randomBytes)(bytes).toString("base64url");
|
|
16266
16337
|
}
|
|
16267
|
-
function
|
|
16268
|
-
|
|
16269
|
-
return false;
|
|
16270
|
-
const v = value;
|
|
16271
|
-
return (typeof v.text === 'string' &&
|
|
16272
|
-
v.text.length <= 30000 &&
|
|
16273
|
-
Number.isFinite(v.checkedAt) &&
|
|
16274
|
-
(v.receiptId === undefined || typeof v.receiptId === 'string') &&
|
|
16275
|
-
(v.proposal === undefined ||
|
|
16276
|
-
(typeof v.proposal === 'object' &&
|
|
16277
|
-
v.proposal !== null &&
|
|
16278
|
-
typeof v.proposal.id === 'string' &&
|
|
16279
|
-
typeof v.proposal.description === 'string')));
|
|
16338
|
+
function hashToken(token) {
|
|
16339
|
+
return (0,external_node_crypto_namespaceObject.createHash)("sha256").update(token).digest("hex");
|
|
16280
16340
|
}
|
|
16281
|
-
|
|
16282
|
-
|
|
16283
|
-
|
|
16284
|
-
if (
|
|
16341
|
+
function sameHash(left, right) {
|
|
16342
|
+
const leftBuffer = Buffer.from(left);
|
|
16343
|
+
const rightBuffer = Buffer.from(right);
|
|
16344
|
+
if (leftBuffer.length !== rightBuffer.length)
|
|
16285
16345
|
return false;
|
|
16286
|
-
|
|
16287
|
-
|
|
16288
|
-
|
|
16289
|
-
|
|
16290
|
-
|
|
16346
|
+
return (0,external_node_crypto_namespaceObject.timingSafeEqual)(leftBuffer, rightBuffer);
|
|
16347
|
+
}
|
|
16348
|
+
function readRecords(pairingsPath) {
|
|
16349
|
+
try {
|
|
16350
|
+
const raw = (0,external_node_fs_namespaceObject.readFileSync)(pairingsPath, "utf8");
|
|
16351
|
+
const parsed = JSON.parse(raw);
|
|
16352
|
+
if (!Array.isArray(parsed))
|
|
16353
|
+
return [];
|
|
16354
|
+
return parsed.filter((entry) => entry &&
|
|
16355
|
+
typeof entry.deviceId === "string" &&
|
|
16356
|
+
typeof entry.deviceName === "string" &&
|
|
16357
|
+
typeof entry.tokenHash === "string" &&
|
|
16358
|
+
typeof entry.createdAt === "string" &&
|
|
16359
|
+
typeof entry.lastSeenAt === "string" &&
|
|
16360
|
+
(typeof entry.revokedAt === "string" || entry.revokedAt === null));
|
|
16361
|
+
}
|
|
16362
|
+
catch {
|
|
16363
|
+
return [];
|
|
16364
|
+
}
|
|
16365
|
+
}
|
|
16366
|
+
function writeRecords(pairingsPath, records) {
|
|
16367
|
+
(0,external_node_fs_namespaceObject.mkdirSync)((0,external_node_path_namespaceObject.dirname)(pairingsPath), { recursive: true });
|
|
16368
|
+
(0,external_node_fs_namespaceObject.writeFileSync)(pairingsPath, `${JSON.stringify(records, null, 2)}\n`, "utf8");
|
|
16369
|
+
}
|
|
16370
|
+
class PairingStore {
|
|
16371
|
+
pairingsPath;
|
|
16372
|
+
records;
|
|
16373
|
+
windows = new Map();
|
|
16374
|
+
constructor(pairingsPath) {
|
|
16375
|
+
this.pairingsPath = pairingsPath;
|
|
16376
|
+
this.records = readRecords(pairingsPath);
|
|
16377
|
+
}
|
|
16378
|
+
list() {
|
|
16379
|
+
return this.records.slice();
|
|
16380
|
+
}
|
|
16381
|
+
pairedCount() {
|
|
16382
|
+
return this.records.filter((record) => !record.revokedAt).length;
|
|
16383
|
+
}
|
|
16384
|
+
openWindow(windowMs = 5 * 60_000, reusable = false) {
|
|
16385
|
+
const pairingId = `pair_${pairing_store_base64Url(8)}`;
|
|
16386
|
+
const pairNonce = pairing_store_base64Url(24);
|
|
16387
|
+
const expiresAt = new Date(Date.now() + windowMs).toISOString();
|
|
16388
|
+
const window = { pairingId, pairNonce, expiresAt, reusable };
|
|
16389
|
+
this.windows.set(pairNonce, window);
|
|
16390
|
+
return window;
|
|
16391
|
+
}
|
|
16392
|
+
consumeWindow(pairNonce) {
|
|
16393
|
+
const window = this.windows.get(pairNonce);
|
|
16394
|
+
if (!window)
|
|
16395
|
+
return null;
|
|
16396
|
+
if (!window.reusable) {
|
|
16397
|
+
this.windows.delete(pairNonce);
|
|
16398
|
+
}
|
|
16399
|
+
if (new Date(window.expiresAt).getTime() < Date.now()) {
|
|
16400
|
+
return null;
|
|
16401
|
+
}
|
|
16402
|
+
return window;
|
|
16403
|
+
}
|
|
16404
|
+
issuePairToken(deviceId, deviceName) {
|
|
16405
|
+
const now = new Date().toISOString();
|
|
16406
|
+
const pairToken = pairing_store_base64Url(32);
|
|
16407
|
+
const nextRecord = {
|
|
16408
|
+
deviceId,
|
|
16409
|
+
deviceName,
|
|
16410
|
+
tokenHash: hashToken(pairToken),
|
|
16411
|
+
createdAt: now,
|
|
16412
|
+
lastSeenAt: now,
|
|
16413
|
+
revokedAt: null,
|
|
16414
|
+
};
|
|
16415
|
+
const existingIndex = this.records.findIndex((record) => record.deviceId === deviceId);
|
|
16416
|
+
if (existingIndex >= 0) {
|
|
16417
|
+
this.records[existingIndex] = nextRecord;
|
|
16418
|
+
}
|
|
16419
|
+
else {
|
|
16420
|
+
this.records.unshift(nextRecord);
|
|
16421
|
+
}
|
|
16422
|
+
writeRecords(this.pairingsPath, this.records);
|
|
16423
|
+
return pairToken;
|
|
16424
|
+
}
|
|
16425
|
+
validatePairToken(deviceId, token, touch = true) {
|
|
16426
|
+
const record = this.records.find((entry) => entry.deviceId === deviceId && !entry.revokedAt);
|
|
16427
|
+
if (!record)
|
|
16428
|
+
return false;
|
|
16429
|
+
const tokenDigest = hashToken(token);
|
|
16430
|
+
if (!sameHash(record.tokenHash, tokenDigest))
|
|
16431
|
+
return false;
|
|
16432
|
+
if (touch) {
|
|
16433
|
+
record.lastSeenAt = new Date().toISOString();
|
|
16434
|
+
writeRecords(this.pairingsPath, this.records);
|
|
16435
|
+
}
|
|
16436
|
+
return true;
|
|
16437
|
+
}
|
|
16438
|
+
validateAnyPairToken(token, touch = true) {
|
|
16439
|
+
const tokenDigest = hashToken(token);
|
|
16440
|
+
const record = this.records.find((entry) => !entry.revokedAt && sameHash(entry.tokenHash, tokenDigest));
|
|
16441
|
+
if (!record)
|
|
16442
|
+
return null;
|
|
16443
|
+
if (touch) {
|
|
16444
|
+
record.lastSeenAt = new Date().toISOString();
|
|
16445
|
+
writeRecords(this.pairingsPath, this.records);
|
|
16446
|
+
}
|
|
16447
|
+
return record;
|
|
16448
|
+
}
|
|
16449
|
+
revoke(deviceId) {
|
|
16450
|
+
const record = this.records.find((entry) => entry.deviceId === deviceId && !entry.revokedAt);
|
|
16451
|
+
if (!record)
|
|
16452
|
+
return false;
|
|
16453
|
+
record.revokedAt = new Date().toISOString();
|
|
16454
|
+
writeRecords(this.pairingsPath, this.records);
|
|
16455
|
+
return true;
|
|
16456
|
+
}
|
|
16457
|
+
reset() {
|
|
16458
|
+
this.records = [];
|
|
16459
|
+
this.windows.clear();
|
|
16460
|
+
writeRecords(this.pairingsPath, this.records);
|
|
16461
|
+
}
|
|
16462
|
+
}
|
|
16463
|
+
//# sourceMappingURL=pairing-store.js.map
|
|
16464
|
+
;// CONCATENATED MODULE: external "node:fs/promises"
|
|
16465
|
+
const external_node_fs_promises_namespaceObject = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:fs/promises");
|
|
16466
|
+
;// CONCATENATED MODULE: external "node:readline"
|
|
16467
|
+
const external_node_readline_namespaceObject = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:readline");
|
|
16468
|
+
;// CONCATENATED MODULE: ../../packages/daemon-core/dist/remote-direct-server.js
|
|
16469
|
+
|
|
16470
|
+
|
|
16471
|
+
|
|
16472
|
+
|
|
16473
|
+
|
|
16474
|
+
const READY_TIMEOUT_MS = 45_000;
|
|
16475
|
+
const PREFERRED_APP_LOOPBACK_PORT = 39_876;
|
|
16476
|
+
const MAX_EVENT_LINE_LENGTH = 8_192;
|
|
16477
|
+
function binaryFilename() {
|
|
16478
|
+
const suffix = process.platform === 'win32' ? '.exe' : '';
|
|
16479
|
+
return `vibelet-remote-direct-${process.platform}-${process.arch}${suffix}`;
|
|
16480
|
+
}
|
|
16481
|
+
function resolveRemoteDirectBinaryCandidates() {
|
|
16482
|
+
const configured = (process.env.REMOSIDIAN_REMOTE_DIRECT_BIN || process.env.VIBELET_REMOTE_DIRECT_BIN)?.trim();
|
|
16483
|
+
const entryDirectory = (0,external_node_path_namespaceObject.dirname)((0,external_node_path_namespaceObject.resolve)(process.argv[1] || process.cwd()));
|
|
16484
|
+
return [
|
|
16485
|
+
...(configured ? [(0,external_node_path_namespaceObject.resolve)(configured)] : []),
|
|
16486
|
+
(0,external_node_path_namespaceObject.join)(entryDirectory, 'remoteDirect', binaryFilename()),
|
|
16487
|
+
(0,external_node_path_namespaceObject.resolve)(entryDirectory, '../dist/remoteDirect', binaryFilename()),
|
|
16488
|
+
];
|
|
16489
|
+
}
|
|
16490
|
+
async function resolveRemoteDirectBinary() {
|
|
16491
|
+
for (const candidate of resolveRemoteDirectBinaryCandidates()) {
|
|
16492
|
+
try {
|
|
16493
|
+
await (0,external_node_fs_promises_namespaceObject.access)(candidate, process.platform === 'win32' ? external_node_fs_namespaceObject.constants.F_OK : external_node_fs_namespaceObject.constants.X_OK);
|
|
16494
|
+
return candidate;
|
|
16495
|
+
}
|
|
16496
|
+
catch {
|
|
16497
|
+
// Try the next deterministic packaged/development location.
|
|
16498
|
+
}
|
|
16499
|
+
}
|
|
16500
|
+
throw new Error(`RemoteDirect sidecar is unavailable for ${process.platform}/${process.arch}`);
|
|
16501
|
+
}
|
|
16502
|
+
function redactCapability(value) {
|
|
16503
|
+
return value.replace(/tc[A-Za-z0-9_-]{16,}/g, '[redacted-remoteDirect-address]').slice(0, 500);
|
|
16504
|
+
}
|
|
16505
|
+
function parseReadyEvent(line, expectedPort) {
|
|
16506
|
+
if (!line || line.length > MAX_EVENT_LINE_LENGTH)
|
|
16507
|
+
return null;
|
|
16508
|
+
try {
|
|
16509
|
+
const parsed = JSON.parse(line);
|
|
16510
|
+
if (parsed.event !== 'server_ready' ||
|
|
16511
|
+
typeof parsed.address !== 'string' ||
|
|
16512
|
+
!parsed.address.startsWith('tc') ||
|
|
16513
|
+
parsed.address.length > 4096 ||
|
|
16514
|
+
parsed.port !== expectedPort) {
|
|
16515
|
+
return null;
|
|
16516
|
+
}
|
|
16517
|
+
return parsed;
|
|
16518
|
+
}
|
|
16519
|
+
catch {
|
|
16520
|
+
return null;
|
|
16521
|
+
}
|
|
16522
|
+
}
|
|
16523
|
+
class RemoteDirectServerProcess {
|
|
16524
|
+
options;
|
|
16525
|
+
child = null;
|
|
16526
|
+
stopping = false;
|
|
16527
|
+
constructor(options) {
|
|
16528
|
+
this.options = options;
|
|
16529
|
+
}
|
|
16530
|
+
get isRunning() {
|
|
16531
|
+
return Boolean(this.child && this.child.exitCode === null && this.child.signalCode === null);
|
|
16532
|
+
}
|
|
16533
|
+
async start() {
|
|
16534
|
+
if (this.child)
|
|
16535
|
+
throw new Error('RemoteDirect sidecar is already started');
|
|
16536
|
+
const binary = await resolveRemoteDirectBinary();
|
|
16537
|
+
this.stopping = false;
|
|
16538
|
+
const child = (0,external_node_child_process_namespaceObject.spawn)(binary, [
|
|
16539
|
+
'server',
|
|
16540
|
+
'--target',
|
|
16541
|
+
`127.0.0.1:${this.options.daemonPort}`,
|
|
16542
|
+
'--remote-port',
|
|
16543
|
+
String(this.options.daemonPort),
|
|
16544
|
+
'--state',
|
|
16545
|
+
this.options.statePath,
|
|
16546
|
+
], { stdio: ['ignore', 'pipe', 'pipe'], windowsHide: true });
|
|
16547
|
+
this.child = child;
|
|
16548
|
+
let stderr = '';
|
|
16549
|
+
child.stderr.setEncoding('utf8');
|
|
16550
|
+
child.stderr.on('data', (chunk) => {
|
|
16551
|
+
stderr = `${stderr}${chunk}`.slice(-2_000);
|
|
16552
|
+
});
|
|
16553
|
+
child.once('exit', (code, signal) => {
|
|
16554
|
+
if (this.child === child)
|
|
16555
|
+
this.child = null;
|
|
16556
|
+
if (!this.stopping)
|
|
16557
|
+
this.options.onUnexpectedExit?.({ code, signal });
|
|
16558
|
+
});
|
|
16559
|
+
const ready = await new Promise((resolveReady, rejectReady) => {
|
|
16560
|
+
const lines = (0,external_node_readline_namespaceObject.createInterface)({ input: child.stdout });
|
|
16561
|
+
const timer = setTimeout(() => {
|
|
16562
|
+
lines.close();
|
|
16563
|
+
rejectReady(new Error('RemoteDirect sidecar startup timed out'));
|
|
16564
|
+
}, READY_TIMEOUT_MS);
|
|
16565
|
+
timer.unref?.();
|
|
16566
|
+
const finish = (error, event) => {
|
|
16567
|
+
clearTimeout(timer);
|
|
16568
|
+
lines.close();
|
|
16569
|
+
child.off('error', onError);
|
|
16570
|
+
child.off('exit', onExitBeforeReady);
|
|
16571
|
+
if (error)
|
|
16572
|
+
rejectReady(error);
|
|
16573
|
+
else if (event)
|
|
16574
|
+
resolveReady(event);
|
|
16575
|
+
};
|
|
16576
|
+
const onError = (error) => finish(new Error(`RemoteDirect sidecar failed to launch: ${redactCapability(error.message)}`));
|
|
16577
|
+
const onExitBeforeReady = (code, signal) => finish(new Error(`RemoteDirect sidecar exited before ready (${code ?? signal ?? 'unknown'}): ${redactCapability(stderr.trim())}`));
|
|
16578
|
+
child.once('error', onError);
|
|
16579
|
+
child.once('exit', onExitBeforeReady);
|
|
16580
|
+
lines.on('line', (line) => {
|
|
16581
|
+
const event = parseReadyEvent(line, this.options.daemonPort);
|
|
16582
|
+
if (event)
|
|
16583
|
+
finish(undefined, event);
|
|
16584
|
+
});
|
|
16585
|
+
}).catch((error) => {
|
|
16586
|
+
this.stop();
|
|
16587
|
+
throw error;
|
|
16588
|
+
});
|
|
16589
|
+
return {
|
|
16590
|
+
kind: 'direct',
|
|
16591
|
+
host: '127.0.0.1',
|
|
16592
|
+
port: PREFERRED_APP_LOOPBACK_PORT,
|
|
16593
|
+
source: 'remoteDirect',
|
|
16594
|
+
stability: 'stable',
|
|
16595
|
+
remoteDirect: {
|
|
16596
|
+
serverAddress: ready.address,
|
|
16597
|
+
remotePort: ready.port,
|
|
16598
|
+
},
|
|
16599
|
+
};
|
|
16600
|
+
}
|
|
16601
|
+
stop() {
|
|
16602
|
+
this.stopping = true;
|
|
16603
|
+
const child = this.child;
|
|
16604
|
+
this.child = null;
|
|
16605
|
+
if (!child || child.exitCode !== null || child.signalCode !== null)
|
|
16606
|
+
return;
|
|
16607
|
+
child.kill('SIGTERM');
|
|
16608
|
+
const forceTimer = setTimeout(() => {
|
|
16609
|
+
if (child.exitCode === null && child.signalCode === null)
|
|
16610
|
+
child.kill('SIGKILL');
|
|
16611
|
+
}, 2_000);
|
|
16612
|
+
forceTimer.unref?.();
|
|
16613
|
+
}
|
|
16614
|
+
}
|
|
16615
|
+
//# sourceMappingURL=remote-direct-server.js.map
|
|
16616
|
+
;// CONCATENATED MODULE: ../../packages/daemon-core/dist/index.js
|
|
16617
|
+
|
|
16618
|
+
|
|
16619
|
+
|
|
16620
|
+
|
|
16621
|
+
|
|
16622
|
+
|
|
16623
|
+
//# sourceMappingURL=index.js.map
|
|
16624
|
+
;// CONCATENATED MODULE: ../../packages/remosidian-protocol/dist/assistant.js
|
|
16625
|
+
function isAssistantStatus(value) {
|
|
16626
|
+
if (!value || typeof value !== 'object')
|
|
16627
|
+
return false;
|
|
16628
|
+
const v = value;
|
|
16629
|
+
return (typeof v.enabled === 'boolean' &&
|
|
16630
|
+
typeof v.connected === 'boolean' &&
|
|
16631
|
+
typeof v.paused === 'boolean' &&
|
|
16632
|
+
Number.isFinite(v.checkedAt) &&
|
|
16633
|
+
['ready', 'disabled', 'paused', 'agent_offline'].includes(v.state) &&
|
|
16634
|
+
(v.summary === undefined || isAssistantSummary(v.summary)));
|
|
16635
|
+
}
|
|
16636
|
+
function isAssistantSummary(value) {
|
|
16637
|
+
if (!value || typeof value !== 'object')
|
|
16638
|
+
return false;
|
|
16639
|
+
const v = value;
|
|
16640
|
+
return (['total', 'active', 'paused', 'blocked', 'review', 'completed'].every((key) => Number.isSafeInteger(v[key]) && Number(v[key]) >= 0) &&
|
|
16641
|
+
(v.runningGoalId === null || typeof v.runningGoalId === 'string') &&
|
|
16642
|
+
Array.isArray(v.focus) &&
|
|
16643
|
+
v.focus.length <= 8 &&
|
|
16644
|
+
v.focus.every((goal) => goal &&
|
|
16645
|
+
typeof goal.id === 'string' &&
|
|
16646
|
+
typeof goal.title === 'string' &&
|
|
16647
|
+
['active', 'paused', 'blocked', 'review', 'completed'].includes(goal.status) &&
|
|
16648
|
+
Number.isSafeInteger(goal.priority) &&
|
|
16649
|
+
typeof goal.summary === 'string' &&
|
|
16650
|
+
typeof goal.blocker === 'string' &&
|
|
16651
|
+
Number.isFinite(goal.updatedAt)));
|
|
16652
|
+
}
|
|
16653
|
+
function isAssistantReply(value) {
|
|
16654
|
+
if (!value || typeof value !== 'object')
|
|
16655
|
+
return false;
|
|
16656
|
+
const v = value;
|
|
16657
|
+
return (typeof v.text === 'string' &&
|
|
16658
|
+
v.text.length <= 30000 &&
|
|
16659
|
+
Number.isFinite(v.checkedAt) &&
|
|
16660
|
+
(v.receiptId === undefined || typeof v.receiptId === 'string') &&
|
|
16661
|
+
(v.proposal === undefined ||
|
|
16662
|
+
(typeof v.proposal === 'object' &&
|
|
16663
|
+
v.proposal !== null &&
|
|
16664
|
+
typeof v.proposal.id === 'string' &&
|
|
16665
|
+
typeof v.proposal.description === 'string')));
|
|
16666
|
+
}
|
|
16667
|
+
//# sourceMappingURL=assistant.js.map
|
|
16668
|
+
;// CONCATENATED MODULE: ../../packages/remosidian-protocol/dist/assistant-signals.js
|
|
16669
|
+
function isAssistantSignals(value) {
|
|
16670
|
+
if (!value || typeof value !== 'object')
|
|
16671
|
+
return false;
|
|
16672
|
+
const v = value;
|
|
16673
|
+
return (typeof v.inboxEnabled === 'boolean' &&
|
|
16674
|
+
typeof v.inboxDirectory === 'string' &&
|
|
16675
|
+
Array.isArray(v.inbox) &&
|
|
16676
|
+
v.inbox.length <= 50 &&
|
|
16291
16677
|
v.inbox.every((x) => x &&
|
|
16292
16678
|
typeof x.id === 'string' &&
|
|
16293
16679
|
typeof x.goalId === 'string' &&
|
|
@@ -16300,7 +16686,7 @@ function isAssistantSignals(value) {
|
|
|
16300
16686
|
v.notifications.every((x) => x &&
|
|
16301
16687
|
typeof x.id === 'string' &&
|
|
16302
16688
|
typeof x.goalId === 'string' &&
|
|
16303
|
-
['decision', 'review', 'failure'].includes(x.kind) &&
|
|
16689
|
+
['started', 'progress', 'decision', 'review', 'blocked', 'failure', 'completed', 'summary'].includes(x.kind) &&
|
|
16304
16690
|
typeof x.title === 'string' &&
|
|
16305
16691
|
typeof x.body === 'string' &&
|
|
16306
16692
|
Number.isFinite(x.createdAt) &&
|
|
@@ -16747,8 +17133,6 @@ function parseRemosidianPush(input) {
|
|
|
16747
17133
|
//# sourceMappingURL=index.js.map
|
|
16748
17134
|
;// CONCATENATED MODULE: external "node:events"
|
|
16749
17135
|
const external_node_events_namespaceObject = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:events");
|
|
16750
|
-
;// CONCATENATED MODULE: external "node:fs/promises"
|
|
16751
|
-
const external_node_fs_promises_namespaceObject = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:fs/promises");
|
|
16752
17136
|
;// CONCATENATED MODULE: external "node:stream"
|
|
16753
17137
|
const external_node_stream_namespaceObject = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:stream");
|
|
16754
17138
|
;// CONCATENATED MODULE: ../../packages/remosidian-server/node_modules/readdirp/index.js
|
|
@@ -18523,7 +18907,7 @@ function createAssistantProVerifier(env = process.env, fetcher = fetch) {
|
|
|
18523
18907
|
const key = env.REVENUECAT_API_KEY;
|
|
18524
18908
|
let validUntil = 0;
|
|
18525
18909
|
return async (customerId) => {
|
|
18526
|
-
if (developmentPro
|
|
18910
|
+
if (developmentPro)
|
|
18527
18911
|
return true;
|
|
18528
18912
|
if (!customer || customer !== customerId || !project || !entitlement || !key)
|
|
18529
18913
|
return false;
|
|
@@ -18675,7 +19059,9 @@ class AssistantPush {
|
|
|
18675
19059
|
!feed.notifications.every((n) => typeof n.id === 'string' &&
|
|
18676
19060
|
Number.isSafeInteger(n.sequence) &&
|
|
18677
19061
|
n.sequence > device.cursor &&
|
|
18678
|
-
['decision', 'review', 'failure'].includes(n.kind) &&
|
|
19062
|
+
['started', 'progress', 'decision', 'review', 'blocked', 'failure', 'completed', 'summary'].includes(n.kind) &&
|
|
19063
|
+
typeof n.title === 'string' &&
|
|
19064
|
+
typeof n.body === 'string' &&
|
|
18679
19065
|
typeof n.read === 'boolean'))
|
|
18680
19066
|
throw Error('Invalid notice feed');
|
|
18681
19067
|
for (const notice of feed.notifications) {
|
|
@@ -18683,6 +19069,7 @@ class AssistantPush {
|
|
|
18683
19069
|
device.queue.push({
|
|
18684
19070
|
noticeId: notice.id,
|
|
18685
19071
|
sequence: notice.sequence,
|
|
19072
|
+
title: notice.title.slice(0, 120),
|
|
18686
19073
|
state: 'queued',
|
|
18687
19074
|
attempts: 0,
|
|
18688
19075
|
nextAt: this.now(),
|
|
@@ -18730,8 +19117,8 @@ class AssistantPush {
|
|
|
18730
19117
|
try {
|
|
18731
19118
|
const result = await this.request('send', {
|
|
18732
19119
|
to: device.token,
|
|
18733
|
-
title: 'Remosidian
|
|
18734
|
-
body: '
|
|
19120
|
+
title: ticket.title || 'Remosidian Personal Agent',
|
|
19121
|
+
body: '打开置顶会话查看详情。',
|
|
18735
19122
|
sound: 'default',
|
|
18736
19123
|
channelId: 'personal-assistant',
|
|
18737
19124
|
data: {
|
|
@@ -30506,12 +30893,12 @@ class RemosidianAssistant {
|
|
|
30506
30893
|
(0,external_node_path_namespaceObject.join)((0,external_node_os_namespaceObject.homedir)(), 'Library', 'Application Support', 'LifeOS Agent');
|
|
30507
30894
|
this.port = options.agentPort ?? Number(process.env.LIFEOS_AGENT_PORT || 4317);
|
|
30508
30895
|
if (!Number.isInteger(this.port) || this.port < 1024 || this.port > 65535)
|
|
30509
|
-
throw new Error('
|
|
30896
|
+
throw new Error('Personal Agent 端口无效。');
|
|
30510
30897
|
this.ready = (0,external_node_fs_promises_namespaceObject.readFile)((0,external_node_path_namespaceObject.join)(options.dataDir, 'assistant-settings.json'), 'utf8')
|
|
30511
30898
|
.then((raw) => {
|
|
30512
30899
|
const ids = JSON.parse(raw);
|
|
30513
30900
|
if (!Array.isArray(ids) || !ids.every((id) => typeof id === 'string'))
|
|
30514
|
-
throw new Error('
|
|
30901
|
+
throw new Error('Personal Agent 设置无效。');
|
|
30515
30902
|
this.enabled = new Set(ids);
|
|
30516
30903
|
})
|
|
30517
30904
|
.catch((error) => {
|
|
@@ -30538,7 +30925,7 @@ class RemosidianAssistant {
|
|
|
30538
30925
|
async handle(req, signal) {
|
|
30539
30926
|
await this.ready;
|
|
30540
30927
|
if (this.loadError)
|
|
30541
|
-
throw new Error('
|
|
30928
|
+
throw new Error('Personal Agent 设置无法读取,请在电脑端检查。');
|
|
30542
30929
|
const vault = this.options.config.vaults.find((v) => v.id === req.vaultId);
|
|
30543
30930
|
if (!vault)
|
|
30544
30931
|
throw new VaultRequestError('找不到这个知识库。', 'not_found');
|
|
@@ -30555,16 +30942,16 @@ class RemosidianAssistant {
|
|
|
30555
30942
|
}
|
|
30556
30943
|
// A lapse must not trap users into leaving automatic work running.
|
|
30557
30944
|
if (!pro && !disabling)
|
|
30558
|
-
throw new VaultRequestError('
|
|
30945
|
+
throw new VaultRequestError('Personal Agent 需要有效的 Remosidian Pro 权限;请检查电脑端的订阅验证配置。', 'forbidden');
|
|
30559
30946
|
if (req.op === 'assistant.status')
|
|
30560
30947
|
return this.status(req.vaultId, vault.root, signal);
|
|
30561
30948
|
if (this.busy)
|
|
30562
|
-
throw new Error('
|
|
30949
|
+
throw new Error('Personal Agent 正在处理上一条请求,请稍后重试。');
|
|
30563
30950
|
this.busy = true;
|
|
30564
30951
|
try {
|
|
30565
30952
|
if (req.op === 'assistant.push.register') {
|
|
30566
30953
|
if (req.enabled && !this.enabled.has(req.vaultId))
|
|
30567
|
-
throw Error('
|
|
30954
|
+
throw Error('请先开启 Personal Agent。');
|
|
30568
30955
|
const dashboard = req.enabled
|
|
30569
30956
|
? (await this.call(vault.root, '/api/dashboard', signal))
|
|
30570
30957
|
: null;
|
|
@@ -30580,7 +30967,7 @@ class RemosidianAssistant {
|
|
|
30580
30967
|
if (req.enabled) {
|
|
30581
30968
|
await this.call(vault.root, '/api/dashboard', signal);
|
|
30582
30969
|
if (req.pause)
|
|
30583
|
-
throw new Error('
|
|
30970
|
+
throw new Error('开启 Personal Agent 时不能同时暂停后台。');
|
|
30584
30971
|
}
|
|
30585
30972
|
// Persist disabling first. If pause fails, control remains disabled and the error is explicit.
|
|
30586
30973
|
const next = new Set(this.enabled);
|
|
@@ -30595,7 +30982,7 @@ class RemosidianAssistant {
|
|
|
30595
30982
|
return this.status(req.vaultId, vault.root, signal);
|
|
30596
30983
|
}
|
|
30597
30984
|
if (!this.enabled.has(req.vaultId))
|
|
30598
|
-
throw new VaultRequestError('
|
|
30985
|
+
throw new VaultRequestError('请先在设置中开启 Personal Agent。', 'forbidden');
|
|
30599
30986
|
if (req.op === 'assistant.signals' ||
|
|
30600
30987
|
req.op === 'assistant.inbox.configure' ||
|
|
30601
30988
|
req.op === 'assistant.notice.read') {
|
|
@@ -30626,7 +31013,7 @@ class RemosidianAssistant {
|
|
|
30626
31013
|
: { vaultRoot: vault.root, proposalId: req.proposalId };
|
|
30627
31014
|
const reply = await this.call(vault.root, path, signal, input);
|
|
30628
31015
|
if (!isAssistantReply(reply))
|
|
30629
|
-
throw new Error('
|
|
31016
|
+
throw new Error('电脑返回的 Personal Agent 结果格式无效。');
|
|
30630
31017
|
return reply;
|
|
30631
31018
|
}
|
|
30632
31019
|
finally {
|
|
@@ -30636,15 +31023,50 @@ class RemosidianAssistant {
|
|
|
30636
31023
|
async status(vaultId, root, signal) {
|
|
30637
31024
|
try {
|
|
30638
31025
|
const result = (await this.call(root, '/api/dashboard', signal));
|
|
31026
|
+
const goals = Array.isArray(result.goals) ? result.goals : [];
|
|
31027
|
+
const counts = (status) => goals.filter((goal) => goal.status === status).length;
|
|
31028
|
+
const enabled = this.enabled.has(vaultId);
|
|
31029
|
+
const paused = result.settings.paused;
|
|
30639
31030
|
return {
|
|
30640
|
-
enabled
|
|
31031
|
+
enabled,
|
|
30641
31032
|
connected: true,
|
|
30642
|
-
paused
|
|
31033
|
+
paused,
|
|
30643
31034
|
checkedAt: Date.now(),
|
|
31035
|
+
state: !enabled ? 'disabled' : paused ? 'paused' : 'ready',
|
|
31036
|
+
summary: {
|
|
31037
|
+
total: goals.length,
|
|
31038
|
+
active: counts('active'),
|
|
31039
|
+
paused: counts('paused'),
|
|
31040
|
+
blocked: counts('blocked'),
|
|
31041
|
+
review: counts('review'),
|
|
31042
|
+
completed: counts('completed'),
|
|
31043
|
+
runningGoalId: result.runningGoalId,
|
|
31044
|
+
focus: goals
|
|
31045
|
+
.filter((goal) => goal.status !== 'completed')
|
|
31046
|
+
.sort((a, b) => Number(b.id === result.runningGoalId) - Number(a.id === result.runningGoalId) ||
|
|
31047
|
+
a.priority - b.priority ||
|
|
31048
|
+
b.updatedAt - a.updatedAt)
|
|
31049
|
+
.slice(0, 4)
|
|
31050
|
+
.map((goal) => ({
|
|
31051
|
+
id: goal.id,
|
|
31052
|
+
title: goal.title.slice(0, 160),
|
|
31053
|
+
status: goal.status,
|
|
31054
|
+
priority: goal.priority,
|
|
31055
|
+
summary: goal.summary.slice(0, 500),
|
|
31056
|
+
blocker: goal.blocker.slice(0, 500),
|
|
31057
|
+
updatedAt: goal.updatedAt,
|
|
31058
|
+
})),
|
|
31059
|
+
},
|
|
30644
31060
|
};
|
|
30645
31061
|
}
|
|
30646
31062
|
catch {
|
|
30647
|
-
return {
|
|
31063
|
+
return {
|
|
31064
|
+
enabled: this.enabled.has(vaultId),
|
|
31065
|
+
connected: false,
|
|
31066
|
+
paused: false,
|
|
31067
|
+
checkedAt: Date.now(),
|
|
31068
|
+
state: 'agent_offline',
|
|
31069
|
+
};
|
|
30648
31070
|
}
|
|
30649
31071
|
}
|
|
30650
31072
|
async save(next) {
|
|
@@ -30657,7 +31079,7 @@ class RemosidianAssistant {
|
|
|
30657
31079
|
async call(root, path, signal, body, method = 'POST') {
|
|
30658
31080
|
const token = (await (0,external_node_fs_promises_namespaceObject.readFile)((0,external_node_path_namespaceObject.join)(this.agentDataDir, 'remosidian-bridge.token'), 'utf8')).trim();
|
|
30659
31081
|
if (!/^[a-f0-9]{64}$/u.test(token))
|
|
30660
|
-
throw new Error('
|
|
31082
|
+
throw new Error('本机 Personal Agent 凭据无效。');
|
|
30661
31083
|
const response = await fetch(`http://127.0.0.1:${this.port}${path}`, {
|
|
30662
31084
|
method: body === undefined ? 'GET' : method,
|
|
30663
31085
|
headers: {
|
|
@@ -30671,7 +31093,7 @@ class RemosidianAssistant {
|
|
|
30671
31093
|
});
|
|
30672
31094
|
const result = (await response.json());
|
|
30673
31095
|
if (!response.ok)
|
|
30674
|
-
throw new Error(result.error || '
|
|
31096
|
+
throw new Error(result.error || '无法连接电脑上的 Personal Agent。');
|
|
30675
31097
|
if (path === '/api/dashboard' &&
|
|
30676
31098
|
(!result.settings?.vault || (await (0,external_node_fs_promises_namespaceObject.realpath)(result.settings.vault)) !== (await (0,external_node_fs_promises_namespaceObject.realpath)(root))))
|
|
30677
31099
|
throw new Error('知识库连接不一致。');
|
|
@@ -33416,7 +33838,7 @@ function runFileBackedPluginTurn(input, prompt, options, onDelta) {
|
|
|
33416
33838
|
}
|
|
33417
33839
|
//# sourceMappingURL=chat-plugin-runtime.js.map
|
|
33418
33840
|
;// CONCATENATED MODULE: ../../packages/remosidian-server/dist/version.js
|
|
33419
|
-
const VERSION = '0.1.
|
|
33841
|
+
const VERSION = '0.1.13';
|
|
33420
33842
|
//# sourceMappingURL=version.js.map
|
|
33421
33843
|
;// CONCATENATED MODULE: ../../packages/remosidian-server/dist/chat-runtime.js
|
|
33422
33844
|
|
|
@@ -34263,6 +34685,30 @@ class RemosidianProtocolMetrics {
|
|
|
34263
34685
|
}
|
|
34264
34686
|
}
|
|
34265
34687
|
//# sourceMappingURL=protocol-metrics.js.map
|
|
34688
|
+
;// CONCATENATED MODULE: ../../packages/remosidian-server/dist/remote-direct-transport.js
|
|
34689
|
+
|
|
34690
|
+
|
|
34691
|
+
|
|
34692
|
+
async function startRemoteDirectTransport(instance, options, port) {
|
|
34693
|
+
const transport = options.remoteDirect
|
|
34694
|
+
? new RemoteDirectServerProcess({
|
|
34695
|
+
daemonPort: port,
|
|
34696
|
+
statePath: (0,external_node_path_namespaceObject.join)(createRemosidianPaths(options.dataDir).dataDir, 'remote-direct-server.json'),
|
|
34697
|
+
onUnexpectedExit: () => instance.setRemoteDirectConnection(null),
|
|
34698
|
+
})
|
|
34699
|
+
: null;
|
|
34700
|
+
try {
|
|
34701
|
+
if (transport)
|
|
34702
|
+
instance.setRemoteDirectConnection(await transport.start());
|
|
34703
|
+
}
|
|
34704
|
+
catch (error) {
|
|
34705
|
+
await new Promise((resolve) => instance.webSocketServer.close(() => instance.httpServer.close(() => resolve())));
|
|
34706
|
+
await instance.dispose();
|
|
34707
|
+
throw error;
|
|
34708
|
+
}
|
|
34709
|
+
return transport;
|
|
34710
|
+
}
|
|
34711
|
+
//# sourceMappingURL=remote-direct-transport.js.map
|
|
34266
34712
|
;// CONCATENATED MODULE: ../../packages/remosidian-server/dist/request-handler.js
|
|
34267
34713
|
|
|
34268
34714
|
|
|
@@ -34310,7 +34756,7 @@ async function handleRemosidianRequest(req, vaultService, chatService, conversat
|
|
|
34310
34756
|
case 'assistant.notice.read':
|
|
34311
34757
|
case 'assistant.push.register':
|
|
34312
34758
|
if (!context.assistant)
|
|
34313
|
-
return invalidResponse(req.id, '
|
|
34759
|
+
return invalidResponse(req.id, '此服务器尚未支持 Personal Agent,请更新电脑端。');
|
|
34314
34760
|
return okResponse(req.id, await context.assistant.handle(req, context.signal));
|
|
34315
34761
|
case 'vault.list':
|
|
34316
34762
|
return okResponse(req.id, await vaultService.list(req.vaultId, req.dir));
|
|
@@ -34754,6 +35200,7 @@ const _parseCodexRateLimitsForTesting = (/* unused pure expression or super */ n
|
|
|
34754
35200
|
|
|
34755
35201
|
|
|
34756
35202
|
|
|
35203
|
+
|
|
34757
35204
|
function server_sendJson(res, statusCode, data) {
|
|
34758
35205
|
const body = `${JSON.stringify(data)}\n`;
|
|
34759
35206
|
res.writeHead(statusCode, {
|
|
@@ -34923,13 +35370,19 @@ function createRemosidianServer(options = {}) {
|
|
|
34923
35370
|
subscriptionWatchers.delete(key);
|
|
34924
35371
|
void existing.watcher.close();
|
|
34925
35372
|
};
|
|
35373
|
+
let remoteDirectConnection = null;
|
|
34926
35374
|
const startedAt = Date.now();
|
|
34927
|
-
const getAdvertisedConnectionTarget = () =>
|
|
34928
|
-
|
|
34929
|
-
|
|
34930
|
-
|
|
34931
|
-
|
|
34932
|
-
|
|
35375
|
+
const getAdvertisedConnectionTarget = () => {
|
|
35376
|
+
const target = computeAdvertisedConnectionTarget({
|
|
35377
|
+
canonicalHost: advertisedHosts.canonicalHost,
|
|
35378
|
+
fallbackHosts: advertisedHosts.fallbackHosts,
|
|
35379
|
+
port: config.server.port,
|
|
35380
|
+
relayUrl,
|
|
35381
|
+
});
|
|
35382
|
+
return remoteDirectConnection
|
|
35383
|
+
? { ...target, connections: [remoteDirectConnection, ...target.connections] }
|
|
35384
|
+
: target;
|
|
35385
|
+
};
|
|
34933
35386
|
const buildHealth = () => {
|
|
34934
35387
|
const target = getAdvertisedConnectionTarget();
|
|
34935
35388
|
return {
|
|
@@ -34940,6 +35393,7 @@ function createRemosidianServer(options = {}) {
|
|
|
34940
35393
|
canonicalHost: target.canonicalHost,
|
|
34941
35394
|
fallbackHosts: target.fallbackHosts,
|
|
34942
35395
|
port: target.port,
|
|
35396
|
+
remoteDirect: { enabled: options.remoteDirect === true, running: Boolean(remoteDirectConnection) },
|
|
34943
35397
|
runtime: hostRuntime,
|
|
34944
35398
|
...(relayUrl ? { relayUrl } : {}),
|
|
34945
35399
|
connections: target.connections,
|
|
@@ -34952,6 +35406,9 @@ function createRemosidianServer(options = {}) {
|
|
|
34952
35406
|
wsClients: webSocketServer.clients.size,
|
|
34953
35407
|
protocol: protocolMetrics.snapshot(),
|
|
34954
35408
|
uptimeSeconds: Math.round((Date.now() - startedAt) / 1000),
|
|
35409
|
+
features: {
|
|
35410
|
+
personalAgent: config.server.features?.personalAgent === true,
|
|
35411
|
+
},
|
|
34955
35412
|
ai: chatService.configured
|
|
34956
35413
|
? {
|
|
34957
35414
|
configured: true,
|
|
@@ -35144,6 +35601,9 @@ function createRemosidianServer(options = {}) {
|
|
|
35144
35601
|
identity,
|
|
35145
35602
|
pairings,
|
|
35146
35603
|
advertisedHosts,
|
|
35604
|
+
setRemoteDirectConnection: (target) => {
|
|
35605
|
+
remoteDirectConnection = target;
|
|
35606
|
+
},
|
|
35147
35607
|
dispose: async () => {
|
|
35148
35608
|
await assistant.close();
|
|
35149
35609
|
await Promise.all([...subscriptionWatchers.values()].map((entry) => entry.watcher.close()));
|
|
@@ -35170,10 +35630,14 @@ async function startRemosidianServer(options = {}) {
|
|
|
35170
35630
|
});
|
|
35171
35631
|
const address = instance.httpServer.address();
|
|
35172
35632
|
const port = address?.port ?? listenPort;
|
|
35633
|
+
instance.config.server.port = port;
|
|
35634
|
+
const transport = await startRemoteDirectTransport(instance, options, port);
|
|
35173
35635
|
return {
|
|
35174
35636
|
...instance,
|
|
35175
35637
|
port,
|
|
35176
35638
|
close: async () => {
|
|
35639
|
+
transport?.stop();
|
|
35640
|
+
instance.setRemoteDirectConnection(null);
|
|
35177
35641
|
await new Promise((resolve, reject) => {
|
|
35178
35642
|
// WebSocketServer.close waits for clients; mobile clients can otherwise
|
|
35179
35643
|
// keep plugin stop/restart pending indefinitely.
|
|
@@ -36306,10 +36770,11 @@ function applyConnectionConfig(config, { relayUrl, advertisedHost, fallbackHosts
|
|
|
36306
36770
|
...(localOnly ? { localOnly: true } : {}),
|
|
36307
36771
|
}
|
|
36308
36772
|
: currentAdvertisedHosts;
|
|
36773
|
+
const { relayUrl: _savedRelay, ...server } = config.server ?? {};
|
|
36309
36774
|
return {
|
|
36310
36775
|
...config,
|
|
36311
36776
|
server: {
|
|
36312
|
-
...
|
|
36777
|
+
...server,
|
|
36313
36778
|
advertisedHosts,
|
|
36314
36779
|
...(relayUrl && !localOnly ? { relayUrl } : {}),
|
|
36315
36780
|
},
|
|
@@ -36337,6 +36802,7 @@ async function resolveRelayForStart(
|
|
|
36337
36802
|
const advertisedHost = isWildcardBindHost(flags.host) ? '' : normalizeOptionalString(flags.host);
|
|
36338
36803
|
const fallbackHosts = readCsvList(flags['fallback-hosts']);
|
|
36339
36804
|
const shouldManageTunnel =
|
|
36805
|
+
flags.cloudflare === true &&
|
|
36340
36806
|
!localOnly &&
|
|
36341
36807
|
explicitRelay === null &&
|
|
36342
36808
|
!advertisedHost &&
|
|
@@ -36374,7 +36840,7 @@ async function resolveRelayForStart(
|
|
|
36374
36840
|
clearRelayConfig(relayConfigPath);
|
|
36375
36841
|
process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
|
|
36376
36842
|
process.stderr.write(
|
|
36377
|
-
`Continuing with LAN
|
|
36843
|
+
`Continuing with Remote Direct and LAN pairing. Use \`npx ${ALIAS_NPM_PACKAGE} start --cloudflare --force\` after fixing Cloudflare Tunnel, or pass --relay=<url> for a custom tunnel.\n`,
|
|
36378
36844
|
);
|
|
36379
36845
|
}
|
|
36380
36846
|
}
|
|
@@ -36389,7 +36855,8 @@ async function resolveRelayForStart(
|
|
|
36389
36855
|
}
|
|
36390
36856
|
|
|
36391
36857
|
const ignoreSavedRelay =
|
|
36392
|
-
explicitRelay === null &&
|
|
36858
|
+
explicitRelay === null &&
|
|
36859
|
+
(flags.cloudflare !== true || Boolean(advertisedHost) || fallbackHosts.length > 0 || managedTunnelFailed);
|
|
36393
36860
|
const relayUrl =
|
|
36394
36861
|
explicitRelay !== null
|
|
36395
36862
|
? explicitRelay
|
|
@@ -36399,75 +36866,17 @@ async function resolveRelayForStart(
|
|
|
36399
36866
|
|
|
36400
36867
|
|
|
36401
36868
|
|
|
36402
|
-
|
|
36403
|
-
|
|
36404
|
-
|
|
36405
|
-
|
|
36406
|
-
|
|
36407
|
-
|
|
36408
|
-
|
|
36409
|
-
|
|
36410
|
-
|
|
36411
|
-
|
|
36412
|
-
|
|
36413
|
-
|
|
36414
|
-
|
|
36869
|
+
function shouldRestartDaemon(health, flags, environment = process.env) {
|
|
36870
|
+
if (flags.force === true) return true;
|
|
36871
|
+
const remoteDirect = flags.local !== true;
|
|
36872
|
+
if (health.remoteDirect?.enabled !== remoteDirect || (remoteDirect && health.remoteDirect?.running !== true))
|
|
36873
|
+
return true;
|
|
36874
|
+
const wantsRelay = flags.cloudflare === true || Boolean(flags.relay) || Boolean(environment.REMOSIDIAN_RELAY_URL);
|
|
36875
|
+
return Boolean(health.relayUrl) !== wantsRelay;
|
|
36876
|
+
}
|
|
36415
36877
|
|
|
36416
|
-
|
|
36417
|
-
const
|
|
36418
|
-
const PRIMARY_NPM_PACKAGE = '@remosidian/cli';
|
|
36419
|
-
const remosidian_ALIAS_NPM_PACKAGE = 'remosidian';
|
|
36420
|
-
|
|
36421
|
-
function helpText() {
|
|
36422
|
-
return [
|
|
36423
|
-
`${remosidian_PROG} v${VERSION} — remote vault server for Obsidian / LifeOS`,
|
|
36424
|
-
'',
|
|
36425
|
-
'Package names:',
|
|
36426
|
-
` ${PRIMARY_NPM_PACKAGE}`,
|
|
36427
|
-
` ${remosidian_ALIAS_NPM_PACKAGE}`,
|
|
36428
|
-
'',
|
|
36429
|
-
'Usage:',
|
|
36430
|
-
` npx ${remosidian_ALIAS_NPM_PACKAGE} Start the current directory as a vault`,
|
|
36431
|
-
` npx ${remosidian_ALIAS_NPM_PACKAGE} <command> [options]`,
|
|
36432
|
-
` ${remosidian_PROG} <command> [options]`,
|
|
36433
|
-
'',
|
|
36434
|
-
'Commands:',
|
|
36435
|
-
' start Start or reuse the Remosidian background daemon',
|
|
36436
|
-
' pair Open a one-time pairing window and print a QR code',
|
|
36437
|
-
' status Print the running daemon health snapshot',
|
|
36438
|
-
' stop Stop the background daemon',
|
|
36439
|
-
' reset-pairings Revoke every paired device (local only)',
|
|
36440
|
-
' init Create ~/.remosidian/config.json interactively',
|
|
36441
|
-
' help Show this help',
|
|
36442
|
-
'',
|
|
36443
|
-
'Global options:',
|
|
36444
|
-
' --data-dir <path> Override $REMOSIDIAN_HOME (default: ~/.remosidian)',
|
|
36445
|
-
' --config <path> Override $REMOSIDIAN_CONFIG (default: <data-dir>/config.json)',
|
|
36446
|
-
' --version Print version and exit',
|
|
36447
|
-
'',
|
|
36448
|
-
'start options:',
|
|
36449
|
-
' --vault <id>:<path> Use an ad-hoc vault instead of the config file',
|
|
36450
|
-
' (repeatable; id defaults to folder name)',
|
|
36451
|
-
' --port <number> Listen on this TCP port (default 41234)',
|
|
36452
|
-
' --access=local LAN/local only; skip Cloudflare Tunnel and saved relay config',
|
|
36453
|
-
' --access=remote Use the default managed Cloudflare Tunnel path',
|
|
36454
|
-
' --access=https://<url>',
|
|
36455
|
-
' Use a custom relay URL for remote access',
|
|
36456
|
-
' --access=<host>[,<fallback>]',
|
|
36457
|
-
' Use direct LAN/Tailscale hosts',
|
|
36458
|
-
' --access= Clear the saved relay URL for this run',
|
|
36459
|
-
' --force Restart the managed Cloudflare Tunnel before startup',
|
|
36460
|
-
' --bind <address> Bind to this interface (default 0.0.0.0)',
|
|
36461
|
-
' --agent <name> AI agent for /Ask: claude or codex (auto-detects)',
|
|
36462
|
-
' --agent-path <path> Override the agent binary path',
|
|
36463
|
-
' --foreground Run the server in the foreground',
|
|
36464
|
-
' --json Emit a single JSON line instead of human output',
|
|
36465
|
-
' --no-qr Skip the pairing QR on startup',
|
|
36466
|
-
'',
|
|
36467
|
-
'Legacy access aliases still work:',
|
|
36468
|
-
' --local, --remote, --tunnel, --relay <url>, --host <address>, --fallback-hosts <x>',
|
|
36469
|
-
].join('\n');
|
|
36470
|
-
}
|
|
36878
|
+
;// CONCATENATED MODULE: ./bin/cli-args.mjs
|
|
36879
|
+
const cli_args_PROG = 'remosidian';
|
|
36471
36880
|
|
|
36472
36881
|
function parseArgs(argv) {
|
|
36473
36882
|
const args = { positionals: [], flags: {}, vaults: [] };
|
|
@@ -36498,8 +36907,8 @@ function parseArgs(argv) {
|
|
|
36498
36907
|
return args;
|
|
36499
36908
|
}
|
|
36500
36909
|
|
|
36501
|
-
function
|
|
36502
|
-
process.stderr.write(`[${
|
|
36910
|
+
function cli_args_failUsage(message, details = '') {
|
|
36911
|
+
process.stderr.write(`[${cli_args_PROG}] ${message}\n`);
|
|
36503
36912
|
if (details) {
|
|
36504
36913
|
process.stderr.write(`${details}\n`);
|
|
36505
36914
|
}
|
|
@@ -36566,7 +36975,7 @@ function normalizeAccessHost(host) {
|
|
|
36566
36975
|
.toLowerCase();
|
|
36567
36976
|
}
|
|
36568
36977
|
|
|
36569
|
-
function parseAccessValue(rawValue, fail =
|
|
36978
|
+
function parseAccessValue(rawValue, fail = cli_args_failUsage) {
|
|
36570
36979
|
if (rawValue === true) {
|
|
36571
36980
|
fail('--access requires an argument (e.g. --access=local)');
|
|
36572
36981
|
}
|
|
@@ -36648,7 +37057,7 @@ function normalizePortValue(rawValue) {
|
|
|
36648
37057
|
return parsed;
|
|
36649
37058
|
}
|
|
36650
37059
|
|
|
36651
|
-
function parsePortFlag(flags, fail =
|
|
37060
|
+
function parsePortFlag(flags, fail = cli_args_failUsage) {
|
|
36652
37061
|
if (!hasOwnFlag(flags, 'port')) return null;
|
|
36653
37062
|
if (flags.port === true) {
|
|
36654
37063
|
fail('--port requires an argument (e.g. --port 41234)');
|
|
@@ -36661,7 +37070,7 @@ function parsePortFlag(flags, fail = failUsage) {
|
|
|
36661
37070
|
return parsed;
|
|
36662
37071
|
}
|
|
36663
37072
|
|
|
36664
|
-
function validateLegacyAccessValues(flags, fail =
|
|
37073
|
+
function validateLegacyAccessValues(flags, fail = cli_args_failUsage) {
|
|
36665
37074
|
for (const name of ['relay', 'host', 'fallback-hosts']) {
|
|
36666
37075
|
if (flags[name] === true) {
|
|
36667
37076
|
const hint =
|
|
@@ -36671,10 +37080,22 @@ function validateLegacyAccessValues(flags, fail = failUsage) {
|
|
|
36671
37080
|
}
|
|
36672
37081
|
}
|
|
36673
37082
|
|
|
36674
|
-
function
|
|
37083
|
+
function resolvePersonalAgentOverride(flags, fail = cli_args_failUsage) {
|
|
37084
|
+
const enable = hasOwnFlag(flags, 'personal-agent') || hasOwnFlag(flags, 'pa');
|
|
37085
|
+
const disable = hasOwnFlag(flags, 'no-personal-agent') || hasOwnFlag(flags, 'no-pa');
|
|
37086
|
+
if (enable && disable) {
|
|
37087
|
+
fail('Do not combine Personal Agent enable and disable flags.');
|
|
37088
|
+
}
|
|
37089
|
+
if (enable) return true;
|
|
37090
|
+
if (disable) return false;
|
|
37091
|
+
return undefined;
|
|
37092
|
+
}
|
|
37093
|
+
|
|
37094
|
+
function normalizeCliFlags(flags, env = process.env, fail = cli_args_failUsage) {
|
|
36675
37095
|
const normalized = { ...flags };
|
|
36676
37096
|
applyNpmConfigArgFallback(normalized, 'port', env);
|
|
36677
37097
|
applyNpmConfigFlagFallback(normalized, 'force', env);
|
|
37098
|
+
applyNpmConfigFlagFallback(normalized, 'cloudflare', env);
|
|
36678
37099
|
|
|
36679
37100
|
const hasAccessFlag = hasOwnFlag(normalized, 'access');
|
|
36680
37101
|
const envAccessValue = hasAccessFlag ? null : readNpmConfigArg('access', env);
|
|
@@ -36707,6 +37128,7 @@ function normalizeCliFlags(flags, env = process.env, fail = failUsage) {
|
|
|
36707
37128
|
delete normalized.tunnel;
|
|
36708
37129
|
validateLegacyAccessValues(normalized, fail);
|
|
36709
37130
|
parsePortFlag(normalized, fail);
|
|
37131
|
+
resolvePersonalAgentOverride(normalized, fail);
|
|
36710
37132
|
return normalized;
|
|
36711
37133
|
}
|
|
36712
37134
|
|
|
@@ -36728,326 +37150,91 @@ function resolveInvocation(argv) {
|
|
|
36728
37150
|
return { command, rest, useCurrentDirectoryVault: false };
|
|
36729
37151
|
}
|
|
36730
37152
|
|
|
36731
|
-
function
|
|
36732
|
-
|
|
36733
|
-
|
|
36734
|
-
|
|
36735
|
-
} catch {
|
|
36736
|
-
return (0,external_node_url_namespaceObject.pathToFileURL)((0,external_node_path_namespaceObject.resolve)(path)).href;
|
|
36737
|
-
}
|
|
36738
|
-
}
|
|
36739
|
-
|
|
36740
|
-
function expandVaultSpec(spec) {
|
|
36741
|
-
if (spec.trim().length === 0) return null;
|
|
36742
|
-
const colon = spec.indexOf(':');
|
|
36743
|
-
let id = '';
|
|
36744
|
-
let root = spec;
|
|
36745
|
-
if (colon > 0 && !/^[a-zA-Z]:[\\/]/.test(spec)) {
|
|
36746
|
-
id = spec.slice(0, colon).trim();
|
|
36747
|
-
root = spec.slice(colon + 1);
|
|
36748
|
-
}
|
|
36749
|
-
if (root.trim().length === 0) return null;
|
|
36750
|
-
const resolved = resolveUserPath(root);
|
|
36751
|
-
if (!id) {
|
|
36752
|
-
id = resolved.split(/[\\/]/).filter(Boolean).pop() ?? 'vault';
|
|
36753
|
-
}
|
|
36754
|
-
return {
|
|
36755
|
-
id,
|
|
36756
|
-
name: id,
|
|
36757
|
-
root: resolved,
|
|
36758
|
-
ignored: ['.obsidian/**', '.git/**', '.DS_Store', '*.swp', '*.tmp'],
|
|
36759
|
-
};
|
|
36760
|
-
}
|
|
36761
|
-
|
|
36762
|
-
function loadConfigOrNull(paths) {
|
|
36763
|
-
if (!(0,external_node_fs_namespaceObject.existsSync)(paths.configPath)) {
|
|
36764
|
-
return null;
|
|
36765
|
-
}
|
|
36766
|
-
try {
|
|
36767
|
-
return JSON.parse((0,external_node_fs_namespaceObject.readFileSync)(paths.configPath, 'utf8'));
|
|
36768
|
-
} catch (error) {
|
|
36769
|
-
console.error(`[${remosidian_PROG}] failed to read ${paths.configPath}: ${error?.message ?? error}`);
|
|
36770
|
-
process.exit(2);
|
|
36771
|
-
}
|
|
36772
|
-
}
|
|
36773
|
-
|
|
36774
|
-
function resolvePaths(flags) {
|
|
36775
|
-
const dataDir = typeof flags['data-dir'] === 'string' ? flags['data-dir'] : undefined;
|
|
36776
|
-
const configPath = typeof flags['config'] === 'string' ? flags['config'] : undefined;
|
|
36777
|
-
if (configPath) {
|
|
36778
|
-
const paths = createRemosidianPaths(dataDir ?? process.env.REMOSIDIAN_HOME ?? '~/.remosidian');
|
|
36779
|
-
return { ...paths, configPath: resolveUserPath(configPath) };
|
|
36780
|
-
}
|
|
36781
|
-
return createRemosidianPaths(dataDir ?? process.env.REMOSIDIAN_HOME ?? '~/.remosidian');
|
|
36782
|
-
}
|
|
36783
|
-
|
|
36784
|
-
function createRuntimePaths(paths) {
|
|
36785
|
-
const runtimeDir = (0,external_node_path_namespaceObject.join)(paths.dataDir, 'runtime', 'current');
|
|
36786
|
-
const logDir = (0,external_node_path_namespaceObject.join)(paths.dataDir, 'logs');
|
|
36787
|
-
return {
|
|
36788
|
-
runtimeDir,
|
|
36789
|
-
runtimeEntryPath: (0,external_node_path_namespaceObject.join)(runtimeDir, 'dist', 'remosidian.mjs'),
|
|
36790
|
-
pidPath: (0,external_node_path_namespaceObject.join)(paths.dataDir, 'daemon.pid'),
|
|
36791
|
-
logDir,
|
|
36792
|
-
stdoutLogPath: (0,external_node_path_namespaceObject.join)(logDir, 'daemon.stdout.log'),
|
|
36793
|
-
stderrLogPath: (0,external_node_path_namespaceObject.join)(logDir, 'daemon.stderr.log'),
|
|
36794
|
-
pairingQrPath: (0,external_node_path_namespaceObject.join)(paths.dataDir, 'pairing-qr.png'),
|
|
36795
|
-
pairingCodePath: (0,external_node_path_namespaceObject.join)(paths.dataDir, 'pairing-code.txt'),
|
|
36796
|
-
};
|
|
36797
|
-
}
|
|
36798
|
-
|
|
36799
|
-
function readPidFile(pidPath) {
|
|
36800
|
-
try {
|
|
36801
|
-
const pid = Number((0,external_node_fs_namespaceObject.readFileSync)(pidPath, 'utf8').trim());
|
|
36802
|
-
return Number.isFinite(pid) && pid > 0 ? pid : null;
|
|
36803
|
-
} catch {
|
|
36804
|
-
return null;
|
|
36805
|
-
}
|
|
36806
|
-
}
|
|
36807
|
-
|
|
36808
|
-
function writePidFile(pidPath, pid) {
|
|
36809
|
-
(0,external_node_fs_namespaceObject.mkdirSync)((0,external_node_path_namespaceObject.dirname)(pidPath), { recursive: true });
|
|
36810
|
-
(0,external_node_fs_namespaceObject.writeFileSync)(pidPath, `${pid}\n`, 'utf8');
|
|
36811
|
-
}
|
|
36812
|
-
|
|
36813
|
-
function removePidFile(pidPath) {
|
|
36814
|
-
(0,external_node_fs_namespaceObject.rmSync)(pidPath, { force: true });
|
|
36815
|
-
}
|
|
36816
|
-
|
|
36817
|
-
function remosidian_isProcessAlive(pid) {
|
|
36818
|
-
try {
|
|
36819
|
-
process.kill(pid, 0);
|
|
36820
|
-
return true;
|
|
36821
|
-
} catch {
|
|
36822
|
-
return false;
|
|
36823
|
-
}
|
|
36824
|
-
}
|
|
36825
|
-
|
|
36826
|
-
function currentEntrypointPath() {
|
|
36827
|
-
const entryPath = (0,external_node_url_namespaceObject.fileURLToPath)(import.meta.url);
|
|
36828
|
-
try {
|
|
36829
|
-
return (0,external_node_fs_namespaceObject.realpathSync)(entryPath);
|
|
36830
|
-
} catch {
|
|
36831
|
-
return entryPath;
|
|
36832
|
-
}
|
|
36833
|
-
}
|
|
36834
|
-
|
|
36835
|
-
function installRuntime(paths) {
|
|
36836
|
-
const runtime = createRuntimePaths(paths);
|
|
36837
|
-
(0,external_node_fs_namespaceObject.mkdirSync)((0,external_node_path_namespaceObject.dirname)(runtime.runtimeEntryPath), { recursive: true });
|
|
36838
|
-
(0,external_node_fs_namespaceObject.copyFileSync)(currentEntrypointPath(), runtime.runtimeEntryPath);
|
|
36839
|
-
(0,external_node_fs_namespaceObject.chmodSync)(runtime.runtimeEntryPath, 0o755);
|
|
36840
|
-
return runtime;
|
|
36841
|
-
}
|
|
36842
|
-
|
|
36843
|
-
function resolveRequestedPort(flags, paths) {
|
|
36844
|
-
return parsePortFlag(flags) ?? loadConfigOrNull(paths)?.server?.port ?? 41234;
|
|
36845
|
-
}
|
|
36846
|
-
|
|
36847
|
-
function renderQrTerminal(payload) {
|
|
36848
|
-
return new Promise((resolveTerm, rejectTerm) => {
|
|
36849
|
-
lib.toString(payload, { type: 'terminal', small: true, errorCorrectionLevel: 'M' }, (err, str) => {
|
|
36850
|
-
if (err) {
|
|
36851
|
-
rejectTerm(err);
|
|
36852
|
-
return;
|
|
36853
|
-
}
|
|
36854
|
-
resolveTerm(str);
|
|
36855
|
-
});
|
|
36856
|
-
});
|
|
36857
|
-
}
|
|
36858
|
-
|
|
36859
|
-
async function writeQrPng(payload, filePath) {
|
|
36860
|
-
(0,external_node_fs_namespaceObject.mkdirSync)((0,external_node_path_namespaceObject.dirname)(filePath), { recursive: true });
|
|
36861
|
-
await lib.toFile(filePath, payload, { type: 'png', errorCorrectionLevel: 'M', margin: 1, scale: 8 });
|
|
36862
|
-
}
|
|
36863
|
-
|
|
36864
|
-
function printHumanHealth(health) {
|
|
36865
|
-
const lines = [];
|
|
36866
|
-
lines.push(`Daemon ${health.daemonId} (${health.displayName})`);
|
|
36867
|
-
lines.push(`Version ${health.version}`);
|
|
36868
|
-
if (health.runtime) {
|
|
36869
|
-
lines.push(`Runtime ${health.runtime === 'cli' ? 'CLI' : 'Obsidian plugin'}`);
|
|
36870
|
-
}
|
|
36871
|
-
lines.push(`Listening http://${health.canonicalHost}:${health.port}`);
|
|
36872
|
-
if (health.relayUrl) {
|
|
36873
|
-
lines.push(`Relay ${health.relayUrl}`);
|
|
36874
|
-
}
|
|
36875
|
-
if (Array.isArray(health.fallbackHosts) && health.fallbackHosts.length > 0) {
|
|
36876
|
-
lines.push(`Fallbacks ${health.fallbackHosts.join(', ')}`);
|
|
36877
|
-
}
|
|
36878
|
-
if (Array.isArray(health.connections) && health.connections.length > 0) {
|
|
36879
|
-
lines.push('Connections:');
|
|
36880
|
-
for (const conn of health.connections) {
|
|
36881
|
-
const proto = conn.secure ? 'wss' : 'ws';
|
|
36882
|
-
lines.push(` - ${proto}://${conn.host}:${conn.port} (${conn.source}, ${conn.stability})`);
|
|
36883
|
-
}
|
|
36884
|
-
}
|
|
36885
|
-
if (Array.isArray(health.vaults) && health.vaults.length > 0) {
|
|
36886
|
-
lines.push('Vaults:');
|
|
36887
|
-
for (const vault of health.vaults) {
|
|
36888
|
-
lines.push(` - ${vault.id}: ${vault.root ?? '(unknown root)'}`);
|
|
36889
|
-
}
|
|
36890
|
-
} else {
|
|
36891
|
-
lines.push('Vaults: (none configured — pass --vault or run `remosidian init`)');
|
|
36892
|
-
}
|
|
36893
|
-
lines.push(`Paired ${health.pairedDevices ?? 0} device(s)`);
|
|
36894
|
-
if (health.ai?.configured) {
|
|
36895
|
-
lines.push(`AI ${health.ai.agent ?? 'agent'} (local CLI)`);
|
|
36896
|
-
} else {
|
|
36897
|
-
lines.push('AI (no claude / codex CLI on PATH — install one to enable Ask)');
|
|
36898
|
-
}
|
|
36899
|
-
return lines.join('\n');
|
|
36900
|
-
}
|
|
36901
|
-
|
|
36902
|
-
function buildPairUri(payload) {
|
|
36903
|
-
// Emit the full schema so @lifeos/client-core's default parser picks it
|
|
36904
|
-
// up. We chose JSON-over-base64url because it survives clipboard paste,
|
|
36905
|
-
// deep links, and QR data URIs without escaping headaches.
|
|
36906
|
-
const full = {
|
|
36907
|
-
type: payload.type,
|
|
36908
|
-
daemonId: payload.daemonId,
|
|
36909
|
-
displayName: payload.displayName,
|
|
36910
|
-
canonicalHost: payload.canonicalHost,
|
|
36911
|
-
port: payload.port,
|
|
36912
|
-
...(payload.fallbackHosts && payload.fallbackHosts.length > 0 ? { fallbackHosts: payload.fallbackHosts } : {}),
|
|
36913
|
-
...(payload.connections && payload.connections.length > 0 ? { connections: payload.connections } : {}),
|
|
36914
|
-
pairNonce: payload.pairNonce,
|
|
36915
|
-
expiresAt: payload.expiresAt,
|
|
36916
|
-
...(payload.runtime ? { runtime: payload.runtime } : {}),
|
|
36917
|
-
...(payload.vaults && payload.vaults.length > 0 ? { vaults: payload.vaults } : {}),
|
|
36918
|
-
};
|
|
36919
|
-
const encoded = Buffer.from(JSON.stringify(full), 'utf8').toString('base64url');
|
|
36920
|
-
return `remosidian://pair?v=1&data=${encoded}`;
|
|
36921
|
-
}
|
|
36922
|
-
|
|
36923
|
-
function normalizePairingConnections(payload) {
|
|
36924
|
-
const explicitConnections = Array.isArray(payload.connections)
|
|
36925
|
-
? payload.connections
|
|
36926
|
-
.filter(
|
|
36927
|
-
(target) =>
|
|
36928
|
-
target &&
|
|
36929
|
-
typeof target.host === 'string' &&
|
|
36930
|
-
Number.isFinite(target.port) &&
|
|
36931
|
-
typeof target.source === 'string' &&
|
|
36932
|
-
typeof target.stability === 'string',
|
|
36933
|
-
)
|
|
36934
|
-
.map((target) => ({
|
|
36935
|
-
kind: target.kind === 'relay' ? 'relay' : 'direct',
|
|
36936
|
-
host: target.host.trim(),
|
|
36937
|
-
port: Math.floor(Number(target.port)),
|
|
36938
|
-
...(target.secure === true ? { secure: true } : {}),
|
|
36939
|
-
source: target.source,
|
|
36940
|
-
stability: target.stability,
|
|
36941
|
-
}))
|
|
36942
|
-
.filter((target) => target.host && target.port > 0)
|
|
36943
|
-
: [];
|
|
36944
|
-
if (explicitConnections.length > 0) {
|
|
36945
|
-
return explicitConnections;
|
|
36946
|
-
}
|
|
36947
|
-
const fallbackHosts = Array.isArray(payload.fallbackHosts)
|
|
36948
|
-
? payload.fallbackHosts.filter((host) => typeof host === 'string' && host.trim()).map((host) => host.trim())
|
|
36949
|
-
: [];
|
|
36950
|
-
const port = Number.isFinite(payload.port) && payload.port > 0 ? Math.floor(payload.port) : 41234;
|
|
36951
|
-
return [
|
|
36952
|
-
{
|
|
36953
|
-
kind: 'direct',
|
|
36954
|
-
host: payload.canonicalHost,
|
|
36955
|
-
port,
|
|
36956
|
-
source: 'configured_host',
|
|
36957
|
-
stability: 'stable',
|
|
36958
|
-
},
|
|
36959
|
-
...fallbackHosts.map((host) => ({
|
|
36960
|
-
kind: 'direct',
|
|
36961
|
-
host,
|
|
36962
|
-
port,
|
|
36963
|
-
source: 'fallback',
|
|
36964
|
-
stability: 'stable',
|
|
36965
|
-
})),
|
|
36966
|
-
];
|
|
36967
|
-
}
|
|
36968
|
-
|
|
36969
|
-
function createCompactPairingPayload(payload) {
|
|
36970
|
-
const compact = {
|
|
36971
|
-
t: 'rp',
|
|
36972
|
-
d: payload.daemonId,
|
|
36973
|
-
n: payload.displayName,
|
|
36974
|
-
h: payload.canonicalHost,
|
|
36975
|
-
p: payload.port,
|
|
36976
|
-
c: payload.pairNonce,
|
|
36977
|
-
e: payload.expiresAt,
|
|
36978
|
-
};
|
|
36979
|
-
if (Array.isArray(payload.fallbackHosts) && payload.fallbackHosts.length > 0) {
|
|
36980
|
-
compact.f = payload.fallbackHosts;
|
|
36981
|
-
}
|
|
36982
|
-
const connections = normalizePairingConnections(payload);
|
|
36983
|
-
if (connections.length > 0) {
|
|
36984
|
-
compact.o = connections.map((target) => [
|
|
36985
|
-
target.kind,
|
|
36986
|
-
target.host,
|
|
36987
|
-
target.port,
|
|
36988
|
-
target.source,
|
|
36989
|
-
target.stability,
|
|
36990
|
-
target.secure === true,
|
|
36991
|
-
]);
|
|
36992
|
-
}
|
|
36993
|
-
if (payload.runtime) {
|
|
36994
|
-
compact.r = payload.runtime;
|
|
36995
|
-
}
|
|
36996
|
-
if (Array.isArray(payload.vaults) && payload.vaults.length > 0) {
|
|
36997
|
-
compact.v = payload.vaults;
|
|
36998
|
-
}
|
|
36999
|
-
return compact;
|
|
37000
|
-
}
|
|
37001
|
-
|
|
37002
|
-
function buildPairCode(payload) {
|
|
37003
|
-
return JSON.stringify(createCompactPairingPayload(payload));
|
|
37004
|
-
}
|
|
37005
|
-
|
|
37006
|
-
function buildPairDeepLink(pairingCode) {
|
|
37007
|
-
return `remosidian://pair?data=${encodeURIComponent(pairingCode)}`;
|
|
37008
|
-
}
|
|
37009
|
-
|
|
37010
|
-
function formatConnectionSummary(target) {
|
|
37011
|
-
const scheme = target.secure === true || target.port === 443 ? 'https' : 'http';
|
|
37012
|
-
return `${scheme}://${target.host}:${target.port} (${target.source}, ${target.stability})`;
|
|
37013
|
-
}
|
|
37014
|
-
|
|
37015
|
-
async function printPairingQr(pairingPayload, paths) {
|
|
37016
|
-
const runtime = createRuntimePaths(paths);
|
|
37017
|
-
const pairingCode = buildPairCode(pairingPayload);
|
|
37018
|
-
const deepLink = buildPairDeepLink(pairingCode);
|
|
37019
|
-
const qr = await renderQrTerminal(pairingCode);
|
|
37020
|
-
(0,external_node_fs_namespaceObject.writeFileSync)(runtime.pairingCodePath, `${pairingCode}\n`, 'utf8');
|
|
37021
|
-
await writeQrPng(pairingCode, runtime.pairingQrPath).catch(() => undefined);
|
|
37022
|
-
process.stdout.write(`\nScan this QR code with the Remosidian app:\n\n${qr}\n`);
|
|
37023
|
-
process.stdout.write(`If the terminal QR is hard to scan, open this PNG instead:\n${runtime.pairingQrPath}\n`);
|
|
37024
|
-
process.stdout.write(`If this device cannot scan, paste this pairing code in Remosidian:\n${pairingCode}\n`);
|
|
37025
|
-
process.stdout.write(`Pairing code saved to:\n${runtime.pairingCodePath}\n`);
|
|
37026
|
-
process.stdout.write(`Or open this deep link on a device with Remosidian installed:\n${deepLink}\n`);
|
|
37027
|
-
}
|
|
37028
|
-
|
|
37029
|
-
async function printPairingSummary(port, paths, health = null, flags = {}) {
|
|
37030
|
-
const healthPayload = health ?? (await waitForHealth(port));
|
|
37031
|
-
if (flags.json) {
|
|
37032
|
-
process.stdout.write(`${JSON.stringify({ ok: true, ...healthPayload })}\n`);
|
|
37033
|
-
return;
|
|
37034
|
-
}
|
|
37035
|
-
process.stdout.write(`Remosidian daemon is ready.\n\n`);
|
|
37036
|
-
process.stdout.write(`${printHumanHealth(healthPayload)}\n`);
|
|
37037
|
-
if (flags['no-qr']) {
|
|
37038
|
-
return;
|
|
37153
|
+
function buildServeArgs(flags) {
|
|
37154
|
+
const args = ['serve'];
|
|
37155
|
+
for (const vault of flags.__vaults ?? []) {
|
|
37156
|
+
args.push('--vault', vault);
|
|
37039
37157
|
}
|
|
37040
|
-
const
|
|
37041
|
-
const
|
|
37042
|
-
|
|
37043
|
-
|
|
37044
|
-
|
|
37045
|
-
|
|
37158
|
+
const skipFlags = new Set(['__vaults', 'foreground', 'json']);
|
|
37159
|
+
for (const [key, value] of Object.entries(flags)) {
|
|
37160
|
+
if (skipFlags.has(key)) continue;
|
|
37161
|
+
if (key === 'no-qr') continue;
|
|
37162
|
+
if (value === true) {
|
|
37163
|
+
args.push(`--${key}`);
|
|
37164
|
+
} else if (typeof value === 'string') {
|
|
37165
|
+
args.push(`--${key}`, value);
|
|
37046
37166
|
}
|
|
37047
37167
|
}
|
|
37048
|
-
|
|
37168
|
+
args.push('--no-qr');
|
|
37169
|
+
return args;
|
|
37170
|
+
}
|
|
37171
|
+
|
|
37172
|
+
|
|
37173
|
+
|
|
37174
|
+
;// CONCATENATED MODULE: ./bin/cli-help.mjs
|
|
37175
|
+
|
|
37176
|
+
const cli_help_PROG = 'remosidian';
|
|
37177
|
+
const PRIMARY_NPM_PACKAGE = '@remosidian/cli';
|
|
37178
|
+
const cli_help_ALIAS_NPM_PACKAGE = 'remosidian';
|
|
37179
|
+
function helpText() {
|
|
37180
|
+
return [
|
|
37181
|
+
`${cli_help_PROG} v${VERSION} — remote vault server for Obsidian / LifeOS`,
|
|
37182
|
+
'',
|
|
37183
|
+
'Package names:',
|
|
37184
|
+
` ${PRIMARY_NPM_PACKAGE}`,
|
|
37185
|
+
` ${cli_help_ALIAS_NPM_PACKAGE}`,
|
|
37186
|
+
'',
|
|
37187
|
+
'Usage:',
|
|
37188
|
+
` npx ${cli_help_ALIAS_NPM_PACKAGE} Start the current directory as a vault`,
|
|
37189
|
+
` npx ${cli_help_ALIAS_NPM_PACKAGE} <command> [options]`,
|
|
37190
|
+
` ${cli_help_PROG} <command> [options]`,
|
|
37191
|
+
'',
|
|
37192
|
+
'Commands:',
|
|
37193
|
+
' start Start or reuse the Remosidian background daemon',
|
|
37194
|
+
' pair Open a one-time pairing window and print a QR code',
|
|
37195
|
+
' status Print the running daemon health snapshot',
|
|
37196
|
+
' stop Stop the background daemon',
|
|
37197
|
+
' reset-pairings Revoke every paired device (local only)',
|
|
37198
|
+
' init Create ~/.remosidian/config.json interactively',
|
|
37199
|
+
' help Show this help',
|
|
37200
|
+
'',
|
|
37201
|
+
'Global options:',
|
|
37202
|
+
' --data-dir <path> Override $REMOSIDIAN_HOME (default: ~/.remosidian)',
|
|
37203
|
+
' --config <path> Override $REMOSIDIAN_CONFIG (default: <data-dir>/config.json)',
|
|
37204
|
+
' --version Print version and exit',
|
|
37205
|
+
'',
|
|
37206
|
+
'start options:',
|
|
37207
|
+
' --vault <id>:<path> Use an ad-hoc vault instead of the config file',
|
|
37208
|
+
' (repeatable; id defaults to folder name)',
|
|
37209
|
+
' --port <number> Listen on this TCP port (default 41234)',
|
|
37210
|
+
' --access=local LAN/local only; skip Cloudflare Tunnel and saved relay config',
|
|
37211
|
+
' --access=remote Use the default Remote Direct path',
|
|
37212
|
+
' --access=https://<url>',
|
|
37213
|
+
' Use a custom relay URL for remote access',
|
|
37214
|
+
' --access=<host>[,<fallback>]',
|
|
37215
|
+
' Use direct LAN/Tailscale hosts',
|
|
37216
|
+
' --access= Clear the saved relay URL for this run',
|
|
37217
|
+
' --force Restart the managed Cloudflare Tunnel before startup',
|
|
37218
|
+
' --bind <address> Bind to this interface (default 0.0.0.0)',
|
|
37219
|
+
' --agent <name> AI agent for /Ask: claude or codex (auto-detects)',
|
|
37220
|
+
' --agent-path <path> Override the agent binary path',
|
|
37221
|
+
' --personal-agent Expose the Personal Agent in paired mobile apps',
|
|
37222
|
+
' --pa Short alias for --personal-agent',
|
|
37223
|
+
' --no-personal-agent Hide the Personal Agent in paired mobile apps',
|
|
37224
|
+
' --no-pa Short alias for --no-personal-agent',
|
|
37225
|
+
' --cloudflare Also enable managed Cloudflare access',
|
|
37226
|
+
' --foreground Run the server in the foreground',
|
|
37227
|
+
' --json Emit a single JSON line instead of human output',
|
|
37228
|
+
' --no-qr Skip the pairing QR on startup',
|
|
37229
|
+
'',
|
|
37230
|
+
'Legacy access aliases still work:',
|
|
37231
|
+
' --local, --remote, --tunnel, --relay <url>, --host <address>, --fallback-hosts <x>',
|
|
37232
|
+
].join('\n');
|
|
37049
37233
|
}
|
|
37050
37234
|
|
|
37235
|
+
|
|
37236
|
+
|
|
37237
|
+
;// CONCATENATED MODULE: ./bin/cli-daemon-api.mjs
|
|
37051
37238
|
async function probeHealth(port, timeoutMs = 0) {
|
|
37052
37239
|
const deadline = Date.now() + timeoutMs;
|
|
37053
37240
|
do {
|
|
@@ -37086,23 +37273,179 @@ async function postJson(port, pathname, body = undefined) {
|
|
|
37086
37273
|
return payload;
|
|
37087
37274
|
}
|
|
37088
37275
|
|
|
37089
|
-
function
|
|
37090
|
-
const
|
|
37091
|
-
|
|
37092
|
-
|
|
37276
|
+
async function waitForDaemonExit(port, timeoutMs = 5_000) {
|
|
37277
|
+
const deadline = Date.now() + timeoutMs;
|
|
37278
|
+
while (Date.now() < deadline) {
|
|
37279
|
+
const health = await probeHealth(port, 0);
|
|
37280
|
+
if (!health) {
|
|
37281
|
+
return true;
|
|
37282
|
+
}
|
|
37283
|
+
await new Promise((resolveWait) => setTimeout(resolveWait, 200));
|
|
37093
37284
|
}
|
|
37094
|
-
|
|
37095
|
-
|
|
37096
|
-
|
|
37097
|
-
|
|
37098
|
-
|
|
37099
|
-
|
|
37100
|
-
|
|
37101
|
-
|
|
37285
|
+
return false;
|
|
37286
|
+
}
|
|
37287
|
+
|
|
37288
|
+
;// CONCATENATED MODULE: ./bin/remosidian.mjs
|
|
37289
|
+
|
|
37290
|
+
|
|
37291
|
+
|
|
37292
|
+
|
|
37293
|
+
|
|
37294
|
+
|
|
37295
|
+
|
|
37296
|
+
|
|
37297
|
+
|
|
37298
|
+
|
|
37299
|
+
|
|
37300
|
+
|
|
37301
|
+
|
|
37302
|
+
|
|
37303
|
+
|
|
37304
|
+
|
|
37305
|
+
|
|
37306
|
+
|
|
37307
|
+
const remosidian_PROG = 'remosidian';
|
|
37308
|
+
const remosidian_ALIAS_NPM_PACKAGE = 'remosidian';
|
|
37309
|
+
|
|
37310
|
+
function resolveEntrypointHref(path) {
|
|
37311
|
+
if (!path) return '';
|
|
37312
|
+
try {
|
|
37313
|
+
return (0,external_node_url_namespaceObject.pathToFileURL)((0,external_node_fs_namespaceObject.realpathSync)((0,external_node_path_namespaceObject.resolve)(path))).href;
|
|
37314
|
+
} catch {
|
|
37315
|
+
return (0,external_node_url_namespaceObject.pathToFileURL)((0,external_node_path_namespaceObject.resolve)(path)).href;
|
|
37316
|
+
}
|
|
37317
|
+
}
|
|
37318
|
+
|
|
37319
|
+
function expandVaultSpec(spec) {
|
|
37320
|
+
if (spec.trim().length === 0) return null;
|
|
37321
|
+
const colon = spec.indexOf(':');
|
|
37322
|
+
let id = '';
|
|
37323
|
+
let root = spec;
|
|
37324
|
+
if (colon > 0 && !/^[a-zA-Z]:[\\/]/.test(spec)) {
|
|
37325
|
+
id = spec.slice(0, colon).trim();
|
|
37326
|
+
root = spec.slice(colon + 1);
|
|
37327
|
+
}
|
|
37328
|
+
if (root.trim().length === 0) return null;
|
|
37329
|
+
const resolved = resolveUserPath(root);
|
|
37330
|
+
if (!id) {
|
|
37331
|
+
id = resolved.split(/[\\/]/).filter(Boolean).pop() ?? 'vault';
|
|
37332
|
+
}
|
|
37333
|
+
return {
|
|
37334
|
+
id,
|
|
37335
|
+
name: id,
|
|
37336
|
+
root: resolved,
|
|
37337
|
+
ignored: ['.obsidian/**', '.git/**', '.DS_Store', '*.swp', '*.tmp'],
|
|
37338
|
+
};
|
|
37339
|
+
}
|
|
37340
|
+
|
|
37341
|
+
function loadConfigOrNull(paths) {
|
|
37342
|
+
if (!(0,external_node_fs_namespaceObject.existsSync)(paths.configPath)) {
|
|
37343
|
+
return null;
|
|
37344
|
+
}
|
|
37345
|
+
try {
|
|
37346
|
+
return JSON.parse((0,external_node_fs_namespaceObject.readFileSync)(paths.configPath, 'utf8'));
|
|
37347
|
+
} catch (error) {
|
|
37348
|
+
console.error(`[${remosidian_PROG}] failed to read ${paths.configPath}: ${error?.message ?? error}`);
|
|
37349
|
+
process.exit(2);
|
|
37350
|
+
}
|
|
37351
|
+
}
|
|
37352
|
+
|
|
37353
|
+
function resolvePaths(flags) {
|
|
37354
|
+
const dataDir = typeof flags['data-dir'] === 'string' ? flags['data-dir'] : undefined;
|
|
37355
|
+
const configPath = typeof flags['config'] === 'string' ? flags['config'] : undefined;
|
|
37356
|
+
if (configPath) {
|
|
37357
|
+
const paths = createRemosidianPaths(dataDir ?? process.env.REMOSIDIAN_HOME ?? '~/.remosidian');
|
|
37358
|
+
return { ...paths, configPath: resolveUserPath(configPath) };
|
|
37359
|
+
}
|
|
37360
|
+
return createRemosidianPaths(dataDir ?? process.env.REMOSIDIAN_HOME ?? '~/.remosidian');
|
|
37361
|
+
}
|
|
37362
|
+
|
|
37363
|
+
function createRuntimePaths(paths) {
|
|
37364
|
+
const runtimeDir = (0,external_node_path_namespaceObject.join)(paths.dataDir, 'runtime', 'current');
|
|
37365
|
+
const logDir = (0,external_node_path_namespaceObject.join)(paths.dataDir, 'logs');
|
|
37366
|
+
return {
|
|
37367
|
+
runtimeDir,
|
|
37368
|
+
runtimeEntryPath: (0,external_node_path_namespaceObject.join)(runtimeDir, 'dist', 'remosidian.mjs'),
|
|
37369
|
+
pidPath: (0,external_node_path_namespaceObject.join)(paths.dataDir, 'daemon.pid'),
|
|
37370
|
+
logDir,
|
|
37371
|
+
stdoutLogPath: (0,external_node_path_namespaceObject.join)(logDir, 'daemon.stdout.log'),
|
|
37372
|
+
stderrLogPath: (0,external_node_path_namespaceObject.join)(logDir, 'daemon.stderr.log'),
|
|
37373
|
+
pairingQrPath: (0,external_node_path_namespaceObject.join)(paths.dataDir, 'pairing-qr.png'),
|
|
37374
|
+
pairingCodePath: (0,external_node_path_namespaceObject.join)(paths.dataDir, 'pairing-code.txt'),
|
|
37375
|
+
};
|
|
37376
|
+
}
|
|
37377
|
+
|
|
37378
|
+
function readPidFile(pidPath) {
|
|
37379
|
+
try {
|
|
37380
|
+
const pid = Number((0,external_node_fs_namespaceObject.readFileSync)(pidPath, 'utf8').trim());
|
|
37381
|
+
return Number.isFinite(pid) && pid > 0 ? pid : null;
|
|
37382
|
+
} catch {
|
|
37383
|
+
return null;
|
|
37384
|
+
}
|
|
37385
|
+
}
|
|
37386
|
+
|
|
37387
|
+
function writePidFile(pidPath, pid) {
|
|
37388
|
+
(0,external_node_fs_namespaceObject.mkdirSync)((0,external_node_path_namespaceObject.dirname)(pidPath), { recursive: true });
|
|
37389
|
+
(0,external_node_fs_namespaceObject.writeFileSync)(pidPath, `${pid}\n`, 'utf8');
|
|
37390
|
+
}
|
|
37391
|
+
|
|
37392
|
+
function removePidFile(pidPath) {
|
|
37393
|
+
(0,external_node_fs_namespaceObject.rmSync)(pidPath, { force: true });
|
|
37394
|
+
}
|
|
37395
|
+
|
|
37396
|
+
function remosidian_isProcessAlive(pid) {
|
|
37397
|
+
try {
|
|
37398
|
+
process.kill(pid, 0);
|
|
37399
|
+
return true;
|
|
37400
|
+
} catch {
|
|
37401
|
+
return false;
|
|
37402
|
+
}
|
|
37403
|
+
}
|
|
37404
|
+
|
|
37405
|
+
function currentEntrypointPath() {
|
|
37406
|
+
const entryPath = (0,external_node_url_namespaceObject.fileURLToPath)(import.meta.url);
|
|
37407
|
+
try {
|
|
37408
|
+
return (0,external_node_fs_namespaceObject.realpathSync)(entryPath);
|
|
37409
|
+
} catch {
|
|
37410
|
+
return entryPath;
|
|
37411
|
+
}
|
|
37412
|
+
}
|
|
37413
|
+
|
|
37414
|
+
function installRuntime(paths) {
|
|
37415
|
+
const runtime = createRuntimePaths(paths);
|
|
37416
|
+
(0,external_node_fs_namespaceObject.mkdirSync)((0,external_node_path_namespaceObject.dirname)(runtime.runtimeEntryPath), { recursive: true });
|
|
37417
|
+
(0,external_node_fs_namespaceObject.copyFileSync)(currentEntrypointPath(), runtime.runtimeEntryPath);
|
|
37418
|
+
const sidecarDir = (0,external_node_path_namespaceObject.join)((0,external_node_path_namespaceObject.dirname)(currentEntrypointPath()), 'remoteDirect');
|
|
37419
|
+
if ((0,external_node_fs_namespaceObject.existsSync)(sidecarDir))
|
|
37420
|
+
(0,external_node_fs_namespaceObject.cpSync)(sidecarDir, (0,external_node_path_namespaceObject.join)((0,external_node_path_namespaceObject.dirname)(runtime.runtimeEntryPath), 'remoteDirect'), { recursive: true });
|
|
37421
|
+
(0,external_node_fs_namespaceObject.chmodSync)(runtime.runtimeEntryPath, 0o755);
|
|
37422
|
+
return runtime;
|
|
37423
|
+
}
|
|
37424
|
+
|
|
37425
|
+
function resolveRequestedPort(flags, paths) {
|
|
37426
|
+
return parsePortFlag(flags) ?? loadConfigOrNull(paths)?.server?.port ?? 41234;
|
|
37427
|
+
}
|
|
37428
|
+
|
|
37429
|
+
async function printPairingSummary(port, paths, health = null, flags = {}) {
|
|
37430
|
+
const healthPayload = health ?? (await waitForHealth(port));
|
|
37431
|
+
if (flags.json) {
|
|
37432
|
+
process.stdout.write(`${JSON.stringify({ ok: true, ...healthPayload })}\n`);
|
|
37433
|
+
return;
|
|
37434
|
+
}
|
|
37435
|
+
process.stdout.write(`Remosidian daemon is ready.\n\n`);
|
|
37436
|
+
process.stdout.write(`${printHumanHealth(healthPayload)}\n`);
|
|
37437
|
+
if (flags['no-qr']) {
|
|
37438
|
+
return;
|
|
37439
|
+
}
|
|
37440
|
+
const pairingPayload = await postJson(port, '/pair/open');
|
|
37441
|
+
const connections = normalizePairingConnections(pairingPayload);
|
|
37442
|
+
if (connections.length > 0) {
|
|
37443
|
+
process.stdout.write('\nPairing paths:\n');
|
|
37444
|
+
for (const target of connections) {
|
|
37445
|
+
process.stdout.write(` - ${formatConnectionSummary(target)}\n`);
|
|
37102
37446
|
}
|
|
37103
37447
|
}
|
|
37104
|
-
|
|
37105
|
-
return args;
|
|
37448
|
+
await printPairingQr(pairingPayload, createRuntimePaths(paths));
|
|
37106
37449
|
}
|
|
37107
37450
|
|
|
37108
37451
|
function startDetachedDaemon(paths, flags) {
|
|
@@ -37138,6 +37481,7 @@ async function cmdServe(flags, paths) {
|
|
|
37138
37481
|
server: {
|
|
37139
37482
|
port: portFlag ?? config?.server?.port ?? 41234,
|
|
37140
37483
|
advertisedHosts: config?.server?.advertisedHosts ?? 'auto',
|
|
37484
|
+
...(config?.server?.features ? { features: config.server.features } : {}),
|
|
37141
37485
|
},
|
|
37142
37486
|
...(config?.ai ? { ai: config.ai } : {}),
|
|
37143
37487
|
};
|
|
@@ -37154,6 +37498,17 @@ async function cmdServe(flags, paths) {
|
|
|
37154
37498
|
process.exit(2);
|
|
37155
37499
|
}
|
|
37156
37500
|
|
|
37501
|
+
const personalAgentOverride = resolvePersonalAgentOverride(flags);
|
|
37502
|
+
if (personalAgentOverride !== undefined) {
|
|
37503
|
+
finalConfig.server = {
|
|
37504
|
+
...finalConfig.server,
|
|
37505
|
+
features: {
|
|
37506
|
+
...(finalConfig.server?.features ?? {}),
|
|
37507
|
+
personalAgent: personalAgentOverride,
|
|
37508
|
+
},
|
|
37509
|
+
};
|
|
37510
|
+
}
|
|
37511
|
+
|
|
37157
37512
|
const agentFlag = typeof flags.agent === 'string' ? flags.agent.trim() : '';
|
|
37158
37513
|
const agentPathFlag =
|
|
37159
37514
|
typeof flags['agent-path'] === 'string' && flags['agent-path'].trim().length > 0 ? flags['agent-path'] : '';
|
|
@@ -37178,6 +37533,7 @@ async function cmdServe(flags, paths) {
|
|
|
37178
37533
|
: '0.0.0.0';
|
|
37179
37534
|
const server = await startRemosidianServer({
|
|
37180
37535
|
config: finalConfig,
|
|
37536
|
+
remoteDirect: connectionConfig.localOnly !== true,
|
|
37181
37537
|
dataDir: paths.dataDir,
|
|
37182
37538
|
host: bindHost,
|
|
37183
37539
|
port,
|
|
@@ -37211,7 +37567,7 @@ async function cmdServe(flags, paths) {
|
|
|
37211
37567
|
const openResp = await fetch(`http://127.0.0.1:${server.port}/pair/open`, { method: 'POST' });
|
|
37212
37568
|
if (openResp.ok) {
|
|
37213
37569
|
const payload = await openResp.json();
|
|
37214
|
-
await printPairingQr(payload, paths);
|
|
37570
|
+
await printPairingQr(payload, createRuntimePaths(paths));
|
|
37215
37571
|
}
|
|
37216
37572
|
} catch (error) {
|
|
37217
37573
|
process.stderr.write(`[${remosidian_PROG}] pair payload not available: ${error?.message ?? error}\n`);
|
|
@@ -37240,7 +37596,29 @@ async function cmdStart(flags, paths) {
|
|
|
37240
37596
|
return;
|
|
37241
37597
|
}
|
|
37242
37598
|
const port = resolveRequestedPort(flags, paths);
|
|
37243
|
-
|
|
37599
|
+
let existing = await probeHealth(port, 1_000);
|
|
37600
|
+
const personalAgentOverride = resolvePersonalAgentOverride(flags);
|
|
37601
|
+
if (existing && personalAgentOverride !== undefined && existing.features?.personalAgent !== personalAgentOverride) {
|
|
37602
|
+
const runtime = createRuntimePaths(paths);
|
|
37603
|
+
const pid = readPidFile(runtime.pidPath);
|
|
37604
|
+
if (!pid || !remosidian_isProcessAlive(pid)) {
|
|
37605
|
+
failUsage(
|
|
37606
|
+
`The daemon on port ${port} was not started by this CLI, so its Personal Agent setting cannot be changed automatically.`,
|
|
37607
|
+
'Stop that foreground/service process, then run this command again.',
|
|
37608
|
+
);
|
|
37609
|
+
}
|
|
37610
|
+
process.kill(pid, 'SIGTERM');
|
|
37611
|
+
const stopped = await waitForDaemonExit(port);
|
|
37612
|
+
if (!stopped) {
|
|
37613
|
+
throw new Error(`Timed out restarting Remosidian daemon on 127.0.0.1:${port}`);
|
|
37614
|
+
}
|
|
37615
|
+
removePidFile(runtime.pidPath);
|
|
37616
|
+
existing = null;
|
|
37617
|
+
}
|
|
37618
|
+
if (existing && shouldRestartDaemon(existing, flags)) {
|
|
37619
|
+
await cmdStop(flags, paths);
|
|
37620
|
+
existing = null;
|
|
37621
|
+
}
|
|
37244
37622
|
if (existing) {
|
|
37245
37623
|
if (!flags.json) {
|
|
37246
37624
|
process.stdout.write('Remosidian daemon is already running. Reusing it.\n\n');
|
|
@@ -37306,18 +37684,6 @@ async function cmdStatus(flags, paths) {
|
|
|
37306
37684
|
}
|
|
37307
37685
|
}
|
|
37308
37686
|
|
|
37309
|
-
async function waitForDaemonExit(port, timeoutMs = 5_000) {
|
|
37310
|
-
const deadline = Date.now() + timeoutMs;
|
|
37311
|
-
while (Date.now() < deadline) {
|
|
37312
|
-
const health = await probeHealth(port, 0);
|
|
37313
|
-
if (!health) {
|
|
37314
|
-
return true;
|
|
37315
|
-
}
|
|
37316
|
-
await new Promise((resolveWait) => setTimeout(resolveWait, 200));
|
|
37317
|
-
}
|
|
37318
|
-
return false;
|
|
37319
|
-
}
|
|
37320
|
-
|
|
37321
37687
|
async function cmdStop(flags, paths) {
|
|
37322
37688
|
const port = resolveRequestedPort(flags, paths);
|
|
37323
37689
|
const runtime = createRuntimePaths(paths);
|
|
@@ -37331,7 +37697,7 @@ async function cmdStop(flags, paths) {
|
|
|
37331
37697
|
await waitForDaemonExit(port);
|
|
37332
37698
|
removePidFile(runtime.pidPath);
|
|
37333
37699
|
stopTunnel({ tunnelStatePath: (0,external_node_path_namespaceObject.join)(paths.dataDir, 'tunnel.json') });
|
|
37334
|
-
process.stdout.write('Remosidian daemon stopped.\n');
|
|
37700
|
+
if (!flags.json) process.stdout.write('Remosidian daemon stopped.\n');
|
|
37335
37701
|
return;
|
|
37336
37702
|
}
|
|
37337
37703
|
removePidFile(runtime.pidPath);
|
|
@@ -37343,7 +37709,7 @@ async function cmdStop(flags, paths) {
|
|
|
37343
37709
|
process.exit(1);
|
|
37344
37710
|
}
|
|
37345
37711
|
stopTunnel({ tunnelStatePath: (0,external_node_path_namespaceObject.join)(paths.dataDir, 'tunnel.json') });
|
|
37346
|
-
process.stdout.write('Remosidian daemon is not running.\n');
|
|
37712
|
+
if (!flags.json) process.stdout.write('Remosidian daemon is not running.\n');
|
|
37347
37713
|
}
|
|
37348
37714
|
|
|
37349
37715
|
async function cmdResetPairings(flags, paths) {
|
|
@@ -37474,4 +37840,5 @@ var __webpack_exports__parseAccessValue = __webpack_exports__.bo;
|
|
|
37474
37840
|
var __webpack_exports__parseArgs = __webpack_exports__.Zy;
|
|
37475
37841
|
var __webpack_exports__resolveEntrypointHref = __webpack_exports__.Mf;
|
|
37476
37842
|
var __webpack_exports__resolveInvocation = __webpack_exports__.t0;
|
|
37477
|
-
|
|
37843
|
+
var __webpack_exports__resolvePersonalAgentOverride = __webpack_exports__.kr;
|
|
37844
|
+
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__hasLegacyAccessArgs as hasLegacyAccessArgs, __webpack_exports__helpText as helpText, __webpack_exports__main as main, __webpack_exports__normalizeCliFlags as normalizeCliFlags, __webpack_exports__normalizePortValue as normalizePortValue, __webpack_exports__parseAccessValue as parseAccessValue, __webpack_exports__parseArgs as parseArgs, __webpack_exports__resolveEntrypointHref as resolveEntrypointHref, __webpack_exports__resolveInvocation as resolveInvocation, __webpack_exports__resolvePersonalAgentOverride as resolvePersonalAgentOverride };
|