@nubisco/openbridge 0.29.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +306 -0
- package/dist/__tests__/homekit-service-type.test.d.ts +2 -0
- package/dist/__tests__/homekit-service-type.test.d.ts.map +1 -0
- package/dist/__tests__/homekit-service-type.test.js +239 -0
- package/dist/__tests__/homekit-service-type.test.js.map +1 -0
- package/dist/__tests__/homekit-visibility.test.d.ts +2 -0
- package/dist/__tests__/homekit-visibility.test.d.ts.map +1 -0
- package/dist/__tests__/homekit-visibility.test.js +157 -0
- package/dist/__tests__/homekit-visibility.test.js.map +1 -0
- package/dist/__tests__/server.test.d.ts +2 -0
- package/dist/__tests__/server.test.d.ts.map +1 -0
- package/dist/__tests__/server.test.js +837 -0
- package/dist/__tests__/server.test.js.map +1 -0
- package/dist/__tests__/timeseries.test.d.ts +2 -0
- package/dist/__tests__/timeseries.test.d.ts.map +1 -0
- package/dist/__tests__/timeseries.test.js +216 -0
- package/dist/__tests__/timeseries.test.js.map +1 -0
- package/dist/auth.d.ts +23 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +171 -0
- package/dist/auth.js.map +1 -0
- package/dist/daemon.d.ts +57 -0
- package/dist/daemon.d.ts.map +1 -0
- package/dist/daemon.js +636 -0
- package/dist/daemon.js.map +1 -0
- package/dist/homekit-service-type.d.ts +82 -0
- package/dist/homekit-service-type.d.ts.map +1 -0
- package/dist/homekit-service-type.js +184 -0
- package/dist/homekit-service-type.js.map +1 -0
- package/dist/homekit-visibility.d.ts +79 -0
- package/dist/homekit-visibility.d.ts.map +1 -0
- package/dist/homekit-visibility.js +164 -0
- package/dist/homekit-visibility.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +62 -0
- package/dist/index.js.map +1 -0
- package/dist/metrics.d.ts +17 -0
- package/dist/metrics.d.ts.map +1 -0
- package/dist/metrics.js +52 -0
- package/dist/metrics.js.map +1 -0
- package/dist/platform-client.d.ts +42 -0
- package/dist/platform-client.d.ts.map +1 -0
- package/dist/platform-client.js +92 -0
- package/dist/platform-client.js.map +1 -0
- package/dist/server.d.ts +30 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +1744 -0
- package/dist/server.js.map +1 -0
- package/dist/timeseries.d.ts +134 -0
- package/dist/timeseries.d.ts.map +1 -0
- package/dist/timeseries.js +378 -0
- package/dist/timeseries.js.map +1 -0
- package/dist/version.d.ts +4 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +28 -0
- package/dist/version.js.map +1 -0
- package/package.json +79 -0
- package/scripts/entrypoint.sh +82 -0
- package/scripts/fix-node-pty.mjs +22 -0
- package/ui-dist/android-chrome-192x192.png +0 -0
- package/ui-dist/android-chrome-512x512.png +0 -0
- package/ui-dist/apple-touch-icon.png +0 -0
- package/ui-dist/assets/index-Q0sxZHIR.css +32 -0
- package/ui-dist/assets/index-ZZYyAzBo.js +106 -0
- package/ui-dist/favicon-16x16.png +0 -0
- package/ui-dist/favicon-32x32.png +0 -0
- package/ui-dist/favicon.ico +0 -0
- package/ui-dist/fonts/MesloLGLDZNerdFontMono-Bold.woff2 +0 -0
- package/ui-dist/fonts/MesloLGLDZNerdFontMono-BoldItalic.woff2 +0 -0
- package/ui-dist/fonts/MesloLGLDZNerdFontMono-Italic.woff2 +0 -0
- package/ui-dist/fonts/MesloLGLDZNerdFontMono-Regular.woff2 +0 -0
- package/ui-dist/index.html +18 -0
- package/ui-dist/logo.svg +30 -0
- package/ui-dist/nubisco-logo.svg +120 -0
- package/ui-dist/site.webmanifest +19 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { OPENBRIDGE_VERSION } from './version.js';
|
|
3
|
+
// Argument handling runs before anything else: `openbridge --help` must never
|
|
4
|
+
// start a daemon or publish a HAP bridge on the local network as a side effect.
|
|
5
|
+
const argv = process.argv.slice(2);
|
|
6
|
+
if (argv.includes('--help') || argv.includes('-h')) {
|
|
7
|
+
console.log(`openbridge ${OPENBRIDGE_VERSION}
|
|
8
|
+
|
|
9
|
+
Local-first home automation bridge. Starts the daemon, the HTTP API, and the
|
|
10
|
+
dashboard, and publishes a HomeKit (HAP) bridge on your local network.
|
|
11
|
+
|
|
12
|
+
Usage:
|
|
13
|
+
openbridge [options]
|
|
14
|
+
|
|
15
|
+
Options:
|
|
16
|
+
-p, --port <port> HTTP API and dashboard port (default: 8582)
|
|
17
|
+
-v, --version Print the version and exit
|
|
18
|
+
-h, --help Print this help and exit
|
|
19
|
+
|
|
20
|
+
Environment:
|
|
21
|
+
OPENBRIDGE_PORT Same as --port
|
|
22
|
+
OPENBRIDGE_HOME State directory for config, plugins and HAP pairing
|
|
23
|
+
(default: ~/.openbridge)
|
|
24
|
+
OPENBRIDGE_UI_PATH Override the location of the built dashboard
|
|
25
|
+
|
|
26
|
+
Docs: https://github.com/nubisco/openbridge#readme`);
|
|
27
|
+
process.exit(0);
|
|
28
|
+
}
|
|
29
|
+
if (argv.includes('--version') || argv.includes('-v')) {
|
|
30
|
+
console.log(OPENBRIDGE_VERSION);
|
|
31
|
+
process.exit(0);
|
|
32
|
+
}
|
|
33
|
+
function parsePort() {
|
|
34
|
+
const i = argv.findIndex((a) => a === '--port' || a === '-p');
|
|
35
|
+
const raw = i !== -1 ? argv[i + 1] : process.env.OPENBRIDGE_PORT;
|
|
36
|
+
if (!raw)
|
|
37
|
+
return undefined;
|
|
38
|
+
const port = parseInt(raw, 10);
|
|
39
|
+
if (!Number.isInteger(port) || port < 1 || port > 65535) {
|
|
40
|
+
console.error(`Invalid port: ${raw}`);
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
return port;
|
|
44
|
+
}
|
|
45
|
+
// Imported dynamically, and only once we know we are actually starting: pulling in
|
|
46
|
+
// the daemon loads Fastify, hap-nodejs and every plugin, which `--help` must not do.
|
|
47
|
+
const { Daemon } = await import('./daemon.js');
|
|
48
|
+
const { Logger } = await import('@nubisco/openbridge-logger');
|
|
49
|
+
const log = Logger.create('system');
|
|
50
|
+
// Prevent plugin errors from crashing the daemon
|
|
51
|
+
process.on('uncaughtException', (err) => {
|
|
52
|
+
log.error(`Uncaught exception: ${err?.message ?? err}`);
|
|
53
|
+
});
|
|
54
|
+
process.on('unhandledRejection', (reason) => {
|
|
55
|
+
log.error(`Unhandled rejection: ${reason instanceof Error ? reason.message : String(reason)}`);
|
|
56
|
+
});
|
|
57
|
+
const daemon = new Daemon();
|
|
58
|
+
daemon.start({ port: parsePort() }).catch((err) => {
|
|
59
|
+
console.error('Fatal error:', err);
|
|
60
|
+
process.exit(1);
|
|
61
|
+
});
|
|
62
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAA;AAEjD,8EAA8E;AAC9E,gFAAgF;AAChF,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;AAElC,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;IACnD,OAAO,CAAC,GAAG,CAAC,cAAc,kBAAkB;;;;;;;;;;;;;;;;;;;mDAmBK,CAAC,CAAA;IAClD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;AACjB,CAAC;AAED,IAAI,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;IACtD,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAA;IAC/B,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;AACjB,CAAC;AAED,SAAS,SAAS;IAChB,MAAM,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,IAAI,CAAC,CAAA;IAC7D,MAAM,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,CAAA;IAChE,IAAI,CAAC,GAAG;QAAE,OAAO,SAAS,CAAA;IAC1B,MAAM,IAAI,GAAG,QAAQ,CAAC,GAAG,EAAE,EAAE,CAAC,CAAA;IAC9B,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,IAAI,GAAG,CAAC,IAAI,IAAI,GAAG,KAAK,EAAE,CAAC;QACxD,OAAO,CAAC,KAAK,CAAC,iBAAiB,GAAG,EAAE,CAAC,CAAA;QACrC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACjB,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC;AAED,mFAAmF;AACnF,qFAAqF;AACrF,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,CAAA;AAC9C,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,MAAM,CAAC,4BAA4B,CAAC,CAAA;AAE7D,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAA;AAEnC,iDAAiD;AACjD,OAAO,CAAC,EAAE,CAAC,mBAAmB,EAAE,CAAC,GAAG,EAAE,EAAE;IACtC,GAAG,CAAC,KAAK,CAAC,uBAAuB,GAAG,EAAE,OAAO,IAAI,GAAG,EAAE,CAAC,CAAA;AACzD,CAAC,CAAC,CAAA;AACF,OAAO,CAAC,EAAE,CAAC,oBAAoB,EAAE,CAAC,MAAM,EAAE,EAAE;IAC1C,GAAG,CAAC,KAAK,CAAC,wBAAwB,MAAM,YAAY,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;AAChG,CAAC,CAAC,CAAA;AAEF,MAAM,MAAM,GAAG,IAAI,MAAM,EAAE,CAAA;AAC3B,MAAM,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IAChD,OAAO,CAAC,KAAK,CAAC,cAAc,EAAE,GAAG,CAAC,CAAA;IAClC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;AACjB,CAAC,CAAC,CAAA"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export interface MetricsSnapshot {
|
|
2
|
+
t: number;
|
|
3
|
+
cpu: number;
|
|
4
|
+
cpuTemp: number;
|
|
5
|
+
memTotal: number;
|
|
6
|
+
memFree: number;
|
|
7
|
+
netRxSec: number;
|
|
8
|
+
netTxSec: number;
|
|
9
|
+
}
|
|
10
|
+
export declare const history: MetricsSnapshot[];
|
|
11
|
+
type Listener = (snap: MetricsSnapshot) => void;
|
|
12
|
+
export declare function onMetrics(fn: Listener): () => void;
|
|
13
|
+
export declare function startMetrics(intervalMs?: number): void;
|
|
14
|
+
export declare function stopMetrics(): void;
|
|
15
|
+
export declare function getHistory(): MetricsSnapshot[];
|
|
16
|
+
export {};
|
|
17
|
+
//# sourceMappingURL=metrics.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metrics.d.ts","sourceRoot":"","sources":["../src/metrics.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,eAAe;IAC9B,CAAC,EAAE,MAAM,CAAA;IACT,GAAG,EAAE,MAAM,CAAA;IACX,OAAO,EAAE,MAAM,CAAA;IACf,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;IACf,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;CACjB;AAGD,eAAO,MAAM,OAAO,EAAE,eAAe,EAAO,CAAA;AAE5C,KAAK,QAAQ,GAAG,CAAC,IAAI,EAAE,eAAe,KAAK,IAAI,CAAA;AAG/C,wBAAgB,SAAS,CAAC,EAAE,EAAE,QAAQ,GAAG,MAAM,IAAI,CAGlD;AA0BD,wBAAgB,YAAY,CAAC,UAAU,SAAO,GAAG,IAAI,CASpD;AAED,wBAAgB,WAAW,IAAI,IAAI,CAKlC;AAED,wBAAgB,UAAU,IAAI,eAAe,EAAE,CAE9C"}
|
package/dist/metrics.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import os from 'os';
|
|
2
|
+
import si from 'systeminformation';
|
|
3
|
+
const MAX_HISTORY = 60;
|
|
4
|
+
export const history = [];
|
|
5
|
+
const listeners = new Set();
|
|
6
|
+
export function onMetrics(fn) {
|
|
7
|
+
listeners.add(fn);
|
|
8
|
+
return () => listeners.delete(fn);
|
|
9
|
+
}
|
|
10
|
+
async function collect() {
|
|
11
|
+
const [load, temp, net] = await Promise.all([
|
|
12
|
+
si.currentLoad().catch(() => ({ currentLoad: 0 })),
|
|
13
|
+
si.cpuTemperature().catch(() => ({ main: -1 })),
|
|
14
|
+
si.networkStats().catch(() => []),
|
|
15
|
+
]);
|
|
16
|
+
const snap = {
|
|
17
|
+
t: Date.now(),
|
|
18
|
+
cpu: Math.round(load.currentLoad ?? 0),
|
|
19
|
+
cpuTemp: Math.round(temp.main && temp.main > 0 ? temp.main : -1),
|
|
20
|
+
memTotal: os.totalmem(),
|
|
21
|
+
memFree: os.freemem(),
|
|
22
|
+
netRxSec: Math.max(0, Math.round(net[0]?.rx_sec ?? 0)),
|
|
23
|
+
netTxSec: Math.max(0, Math.round(net[0]?.tx_sec ?? 0)),
|
|
24
|
+
};
|
|
25
|
+
history.push(snap);
|
|
26
|
+
if (history.length > MAX_HISTORY)
|
|
27
|
+
history.shift();
|
|
28
|
+
return snap;
|
|
29
|
+
}
|
|
30
|
+
let timer = null;
|
|
31
|
+
export function startMetrics(intervalMs = 2000) {
|
|
32
|
+
if (timer)
|
|
33
|
+
return;
|
|
34
|
+
// Prime the CPU sampler (first reading is always 0)
|
|
35
|
+
collect().then(() => {
|
|
36
|
+
timer = setInterval(async () => {
|
|
37
|
+
const snap = await collect();
|
|
38
|
+
for (const fn of listeners)
|
|
39
|
+
fn(snap);
|
|
40
|
+
}, intervalMs);
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
export function stopMetrics() {
|
|
44
|
+
if (timer) {
|
|
45
|
+
clearInterval(timer);
|
|
46
|
+
timer = null;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
export function getHistory() {
|
|
50
|
+
return [...history];
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=metrics.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metrics.js","sourceRoot":"","sources":["../src/metrics.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAA;AACnB,OAAO,EAAE,MAAM,mBAAmB,CAAA;AAYlC,MAAM,WAAW,GAAG,EAAE,CAAA;AACtB,MAAM,CAAC,MAAM,OAAO,GAAsB,EAAE,CAAA;AAG5C,MAAM,SAAS,GAAG,IAAI,GAAG,EAAY,CAAA;AAErC,MAAM,UAAU,SAAS,CAAC,EAAY;IACpC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;IACjB,OAAO,GAAG,EAAE,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;AACnC,CAAC;AAED,KAAK,UAAU,OAAO;IACpB,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QAC1C,EAAE,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,CAAC,CAAC;QAClD,EAAE,CAAC,cAAc,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;QAC/C,EAAE,CAAC,YAAY,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAW,CAAC;KAC3C,CAAC,CAAA;IAEF,MAAM,IAAI,GAAoB;QAC5B,CAAC,EAAE,IAAI,CAAC,GAAG,EAAE;QACb,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,WAAW,IAAI,CAAC,CAAC;QACtC,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAChE,QAAQ,EAAE,EAAE,CAAC,QAAQ,EAAE;QACvB,OAAO,EAAE,EAAE,CAAC,OAAO,EAAE;QACrB,QAAQ,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,MAAM,IAAI,CAAC,CAAC,CAAC;QACtD,QAAQ,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,MAAM,IAAI,CAAC,CAAC,CAAC;KACvD,CAAA;IAED,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAClB,IAAI,OAAO,CAAC,MAAM,GAAG,WAAW;QAAE,OAAO,CAAC,KAAK,EAAE,CAAA;IACjD,OAAO,IAAI,CAAA;AACb,CAAC;AAED,IAAI,KAAK,GAA0C,IAAI,CAAA;AAEvD,MAAM,UAAU,YAAY,CAAC,UAAU,GAAG,IAAI;IAC5C,IAAI,KAAK;QAAE,OAAM;IACjB,oDAAoD;IACpD,OAAO,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE;QAClB,KAAK,GAAG,WAAW,CAAC,KAAK,IAAI,EAAE;YAC7B,MAAM,IAAI,GAAG,MAAM,OAAO,EAAE,CAAA;YAC5B,KAAK,MAAM,EAAE,IAAI,SAAS;gBAAE,EAAE,CAAC,IAAI,CAAC,CAAA;QACtC,CAAC,EAAE,UAAU,CAAC,CAAA;IAChB,CAAC,CAAC,CAAA;AACJ,CAAC;AAED,MAAM,UAAU,WAAW;IACzB,IAAI,KAAK,EAAE,CAAC;QACV,aAAa,CAAC,KAAK,CAAC,CAAA;QACpB,KAAK,GAAG,IAAI,CAAA;IACd,CAAC;AACH,CAAC;AAED,MAAM,UAAU,UAAU;IACxB,OAAO,CAAC,GAAG,OAAO,CAAC,CAAA;AACrB,CAAC"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Inlined from @nubisco/platform-sdk/src/client.ts.
|
|
3
|
+
* Copied directly to avoid pulling a private GitHub Packages dep into this
|
|
4
|
+
* OSS image. Keep in sync with platform/shared/src/client.ts when the
|
|
5
|
+
* platform SDK changes.
|
|
6
|
+
*
|
|
7
|
+
* Verifies RS256 JWTs issued by the Nubisco Platform using the platform's
|
|
8
|
+
* /.well-known/jwks.json. No external dependency required: WebCrypto only.
|
|
9
|
+
*/
|
|
10
|
+
export interface TPlatformClientOptions {
|
|
11
|
+
/** Base URL of the Nubisco Platform, e.g. https://platform.nubisco.io */
|
|
12
|
+
issuer: string;
|
|
13
|
+
/** JWKS cache TTL in milliseconds. Defaults to 300 000 (5 min). */
|
|
14
|
+
cacheTtlMs?: number;
|
|
15
|
+
}
|
|
16
|
+
export interface TJwtClaims {
|
|
17
|
+
sub: string;
|
|
18
|
+
email: string;
|
|
19
|
+
name?: string;
|
|
20
|
+
role: string;
|
|
21
|
+
/** App id (uuid) the token was issued to. */
|
|
22
|
+
app_id?: string;
|
|
23
|
+
/** App slug the token was issued to: matches the app_id used in the SSO request. */
|
|
24
|
+
app?: string;
|
|
25
|
+
plan?: string;
|
|
26
|
+
plan_status?: string;
|
|
27
|
+
iat: number;
|
|
28
|
+
exp: number;
|
|
29
|
+
iss: string;
|
|
30
|
+
}
|
|
31
|
+
export declare class PlatformClient {
|
|
32
|
+
private readonly issuer;
|
|
33
|
+
private readonly cacheTtlMs;
|
|
34
|
+
private jwksCache;
|
|
35
|
+
private importedKeys;
|
|
36
|
+
constructor(options: TPlatformClientOptions);
|
|
37
|
+
private fetchJwks;
|
|
38
|
+
private getVerifyKey;
|
|
39
|
+
private importKey;
|
|
40
|
+
verify(token: string): Promise<TJwtClaims>;
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=platform-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"platform-client.d.ts","sourceRoot":"","sources":["../src/platform-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,WAAW,sBAAsB;IACrC,yEAAyE;IACzE,MAAM,EAAE,MAAM,CAAA;IACd,mEAAmE;IACnE,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAED,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,6CAA6C;IAC7C,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,oFAAoF;IACpF,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,MAAM,CAAA;CACZ;AA6BD,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAQ;IAC/B,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAQ;IACnC,OAAO,CAAC,SAAS,CAAsD;IACvE,OAAO,CAAC,YAAY,CAAoC;gBAE5C,OAAO,EAAE,sBAAsB;YAK7B,SAAS;YAaT,YAAY;YAgBZ,SAAS;IAQjB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;CAsBjD"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Inlined from @nubisco/platform-sdk/src/client.ts.
|
|
3
|
+
* Copied directly to avoid pulling a private GitHub Packages dep into this
|
|
4
|
+
* OSS image. Keep in sync with platform/shared/src/client.ts when the
|
|
5
|
+
* platform SDK changes.
|
|
6
|
+
*
|
|
7
|
+
* Verifies RS256 JWTs issued by the Nubisco Platform using the platform's
|
|
8
|
+
* /.well-known/jwks.json. No external dependency required: WebCrypto only.
|
|
9
|
+
*/
|
|
10
|
+
function base64urlDecode(input) {
|
|
11
|
+
const padded = input.replace(/-/g, '+').replace(/_/g, '/');
|
|
12
|
+
const padLen = (4 - (padded.length % 4)) % 4;
|
|
13
|
+
const b64 = padded + '='.repeat(padLen);
|
|
14
|
+
const binary = atob(b64);
|
|
15
|
+
const bytes = Uint8Array.from(binary, (c) => c.charCodeAt(0));
|
|
16
|
+
return bytes.buffer;
|
|
17
|
+
}
|
|
18
|
+
function base64urlToUtf8(input) {
|
|
19
|
+
return new TextDecoder().decode(new Uint8Array(base64urlDecode(input)));
|
|
20
|
+
}
|
|
21
|
+
export class PlatformClient {
|
|
22
|
+
issuer;
|
|
23
|
+
cacheTtlMs;
|
|
24
|
+
jwksCache = null;
|
|
25
|
+
importedKeys = new Map();
|
|
26
|
+
constructor(options) {
|
|
27
|
+
this.issuer = options.issuer.replace(/\/$/, '');
|
|
28
|
+
this.cacheTtlMs = options.cacheTtlMs ?? 300_000;
|
|
29
|
+
}
|
|
30
|
+
async fetchJwks() {
|
|
31
|
+
const now = Date.now();
|
|
32
|
+
if (this.jwksCache && now - this.jwksCache.fetchedAt < this.cacheTtlMs) {
|
|
33
|
+
return this.jwksCache.keys;
|
|
34
|
+
}
|
|
35
|
+
const res = await fetch(`${this.issuer}/.well-known/jwks.json`);
|
|
36
|
+
if (!res.ok)
|
|
37
|
+
throw new Error(`Failed to fetch JWKS: ${res.status}`);
|
|
38
|
+
const data = (await res.json());
|
|
39
|
+
this.jwksCache = { keys: data.keys, fetchedAt: now };
|
|
40
|
+
this.importedKeys.clear();
|
|
41
|
+
return data.keys;
|
|
42
|
+
}
|
|
43
|
+
async getVerifyKey(kid) {
|
|
44
|
+
const cached = this.importedKeys.get(kid);
|
|
45
|
+
if (cached)
|
|
46
|
+
return cached;
|
|
47
|
+
const keys = await this.fetchJwks();
|
|
48
|
+
const jwk = keys.find((k) => k.kid === kid);
|
|
49
|
+
if (!jwk) {
|
|
50
|
+
// Force-refresh JWKS once on cache miss in case the platform rotated.
|
|
51
|
+
this.jwksCache = null;
|
|
52
|
+
const refreshed = await this.fetchJwks();
|
|
53
|
+
const retried = refreshed.find((k) => k.kid === kid);
|
|
54
|
+
if (!retried)
|
|
55
|
+
throw new Error(`No JWKS key found for kid: ${kid}`);
|
|
56
|
+
return this.importKey(retried);
|
|
57
|
+
}
|
|
58
|
+
return this.importKey(jwk);
|
|
59
|
+
}
|
|
60
|
+
async importKey(jwk) {
|
|
61
|
+
const key = await crypto.subtle.importKey('jwk', jwk, { name: 'RSASSA-PKCS1-v1_5', hash: 'SHA-256' }, false, [
|
|
62
|
+
'verify',
|
|
63
|
+
]);
|
|
64
|
+
this.importedKeys.set(jwk.kid, key);
|
|
65
|
+
return key;
|
|
66
|
+
}
|
|
67
|
+
async verify(token) {
|
|
68
|
+
const parts = token.split('.');
|
|
69
|
+
if (parts.length !== 3)
|
|
70
|
+
throw new Error('Malformed JWT');
|
|
71
|
+
const [headerB64, payloadB64, signatureB64] = parts;
|
|
72
|
+
const header = JSON.parse(base64urlToUtf8(headerB64));
|
|
73
|
+
if (header.alg !== 'RS256')
|
|
74
|
+
throw new Error(`Unsupported algorithm: ${header.alg}`);
|
|
75
|
+
if (!header.kid)
|
|
76
|
+
throw new Error('JWT missing kid');
|
|
77
|
+
const key = await this.getVerifyKey(header.kid);
|
|
78
|
+
const signingInput = new TextEncoder().encode(`${headerB64}.${payloadB64}`);
|
|
79
|
+
const signature = base64urlDecode(signatureB64);
|
|
80
|
+
const valid = await crypto.subtle.verify('RSASSA-PKCS1-v1_5', key, signature, signingInput);
|
|
81
|
+
if (!valid)
|
|
82
|
+
throw new Error('JWT signature verification failed');
|
|
83
|
+
const claims = JSON.parse(base64urlToUtf8(payloadB64));
|
|
84
|
+
const now = Math.floor(Date.now() / 1000);
|
|
85
|
+
if (claims.exp < now)
|
|
86
|
+
throw new Error('JWT expired');
|
|
87
|
+
if (claims.iss !== this.issuer)
|
|
88
|
+
throw new Error(`JWT issuer mismatch: expected ${this.issuer}`);
|
|
89
|
+
return claims;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=platform-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"platform-client.js","sourceRoot":"","sources":["../src/platform-client.ts"],"names":[],"mappings":"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"}
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import Fastify from 'fastify';
|
|
2
|
+
import type { PluginRegistry } from '@nubisco/openbridge-core';
|
|
3
|
+
import type { HomebridgeAPI } from '@nubisco/openbridge-compatibility-homebridge';
|
|
4
|
+
import { OPENBRIDGE_VERSION } from './version.js';
|
|
5
|
+
export { OPENBRIDGE_VERSION };
|
|
6
|
+
import type { HomeKitVisibility } from './homekit-visibility.js';
|
|
7
|
+
import { type HomeKitServiceTypes } from './homekit-service-type.js';
|
|
8
|
+
export interface HapInfo {
|
|
9
|
+
setupURI: string;
|
|
10
|
+
pincode: string;
|
|
11
|
+
}
|
|
12
|
+
export interface LocalPlugin {
|
|
13
|
+
name: string;
|
|
14
|
+
version: string;
|
|
15
|
+
description: string;
|
|
16
|
+
author: string;
|
|
17
|
+
path: string;
|
|
18
|
+
platform?: string;
|
|
19
|
+
displayName?: string;
|
|
20
|
+
}
|
|
21
|
+
export declare function createServer(registry: PluginRegistry, hapAPI?: HomebridgeAPI | null, hapInfo?: HapInfo | null, localPluginSources?: string[], knownHbPackageNames?: Set<string>, controls?: Map<string, (value: unknown) => void | Promise<void>>, restrictedControls?: Set<string>, homekitVisibility?: HomeKitVisibility | null, homekitServiceTypes?: HomeKitServiceTypes | null,
|
|
22
|
+
/**
|
|
23
|
+
* Resolve the HAP accessory a native plugin published for one of its
|
|
24
|
+
* devices. Native plugins add accessories to the bridge directly, so they
|
|
25
|
+
* never appear in /api/accessories; this is how the UI reaches them.
|
|
26
|
+
*/
|
|
27
|
+
resolveNativeAccessory?: ((deviceId: string) => unknown | null) | null,
|
|
28
|
+
/** UUID of the accessory a native plugin published for a device, if any. */
|
|
29
|
+
nativeAccessoryUuid?: ((deviceId: string) => string | null) | null): Promise<Fastify.FastifyInstance<import("http").Server<typeof import("http").IncomingMessage, typeof import("http").ServerResponse>, import("http").IncomingMessage, import("http").ServerResponse<import("http").IncomingMessage>, Fastify.FastifyBaseLogger, Fastify.FastifyTypeProviderDefault>>;
|
|
30
|
+
//# sourceMappingURL=server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,SAAS,CAAA;AAa7B,OAAO,KAAK,EAAE,cAAc,EAA4B,MAAM,0BAA0B,CAAA;AAGxF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,8CAA8C,CAAA;AASjF,OAAO,EAA4B,kBAAkB,EAAE,MAAM,cAAc,CAAA;AAC3E,OAAO,EAAE,kBAAkB,EAAE,CAAA;AAyB7B,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAA;AAChE,OAAO,EAGL,KAAK,mBAAmB,EAEzB,MAAM,2BAA2B,CAAA;AAElC,MAAM,WAAW,OAAO;IACtB,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;IACf,WAAW,EAAE,MAAM,CAAA;IACnB,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAWD,wBAAsB,YAAY,CAChC,QAAQ,EAAE,cAAc,EACxB,MAAM,GAAE,aAAa,GAAG,IAAW,EACnC,OAAO,GAAE,OAAO,GAAG,IAAW,EAC9B,kBAAkB,GAAE,MAAM,EAAO,EACjC,mBAAmB,GAAE,GAAG,CAAC,MAAM,CAAa,EAC5C,QAAQ,GAAE,GAAG,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAa,EAC3E,kBAAkB,GAAE,GAAG,CAAC,MAAM,CAAa,EAC3C,iBAAiB,GAAE,iBAAiB,GAAG,IAAW,EAClD,mBAAmB,GAAE,mBAAmB,GAAG,IAAW;AACtD;;;;GAIG;AACH,sBAAsB,GAAE,CAAC,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,GAAG,IAAI,CAAC,GAAG,IAAW;AAC5E,4EAA4E;AAC5E,mBAAmB,GAAE,CAAC,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC,GAAG,IAAW,sSA2wDzE"}
|