@camstack/system 1.2.428 → 1.2.429
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/dist/agent-https-names-CpUSuP4w.js +120 -0
- package/dist/agent-https-names-uetr4OQl.mjs +91 -0
- package/dist/auth/auth-audit-log.d.ts +1 -1
- package/dist/auth/device-ca-store.d.ts +57 -0
- package/dist/auth/device-ca.d.ts +75 -0
- package/dist/auth/device-credential-manager.d.ts +154 -0
- package/dist/auth/login-session-manager.d.ts +14 -2
- package/dist/auth/user-management-internal.d.ts +10 -1
- package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +1 -1
- package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +1 -1
- package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +1 -1
- package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +1 -1
- package/dist/builtins/alerts/alerts.addon.js +1 -1
- package/dist/builtins/alerts/alerts.addon.mjs +1 -1
- package/dist/builtins/autotrack/index.js +1 -1
- package/dist/builtins/autotrack/index.mjs +1 -1
- package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +2 -2
- package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +2 -2
- package/dist/builtins/calendars/calendars.addon.js +1 -1
- package/dist/builtins/calendars/calendars.addon.mjs +1 -1
- package/dist/builtins/camera-grid/index.js +1 -1
- package/dist/builtins/camera-grid/index.mjs +1 -1
- package/dist/builtins/composer/composer.addon.js +2 -2
- package/dist/builtins/composer/composer.addon.mjs +2 -2
- package/dist/builtins/console-logging/index.js +1 -1
- package/dist/builtins/console-logging/index.mjs +1 -1
- package/dist/builtins/core-blocks/core-blocks.addon.js +2 -2
- package/dist/builtins/core-blocks/core-blocks.addon.mjs +2 -2
- package/dist/builtins/device-manager/device-manager.addon.js +2 -2
- package/dist/builtins/device-manager/device-manager.addon.mjs +2 -2
- package/dist/builtins/hub-forwarder/index.js +1 -1
- package/dist/builtins/hub-forwarder/index.mjs +1 -1
- package/dist/builtins/keychain/index.js +10 -6
- package/dist/builtins/keychain/index.mjs +8 -4
- package/dist/builtins/liveness-monitor/liveness-monitor.addon.js +1 -1
- package/dist/builtins/liveness-monitor/liveness-monitor.addon.mjs +1 -1
- package/dist/builtins/local-auth/local-auth.addon.d.ts +6 -0
- package/dist/builtins/local-auth/local-auth.addon.js +955 -47
- package/dist/builtins/local-auth/local-auth.addon.mjs +946 -41
- package/dist/builtins/local-network/local-network.addon.js +2 -2
- package/dist/builtins/local-network/local-network.addon.mjs +2 -2
- package/dist/builtins/loki-logging/index.js +1 -1
- package/dist/builtins/loki-logging/index.mjs +1 -1
- package/dist/builtins/native-metrics/native-metrics.addon.js +1 -1
- package/dist/builtins/native-metrics/native-metrics.addon.mjs +1 -1
- package/dist/builtins/platform-probe/index.js +1 -1
- package/dist/builtins/platform-probe/index.mjs +1 -1
- package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
- package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
- package/dist/builtins/snapshot/index.js +1 -1
- package/dist/builtins/snapshot/index.mjs +1 -1
- package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
- package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
- package/dist/builtins/sqlite-storage/index.js +1 -1
- package/dist/builtins/sqlite-storage/index.mjs +1 -1
- package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +3 -3
- package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +3 -3
- package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +1 -1
- package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +1 -1
- package/dist/builtins/system-config/system-config.addon.d.ts +18 -0
- package/dist/builtins/system-config/system-config.addon.js +834 -18
- package/dist/builtins/system-config/system-config.addon.mjs +819 -19
- package/dist/builtins/winston-logging/index.js +1 -1
- package/dist/builtins/winston-logging/index.mjs +1 -1
- package/dist/{composition-sources-DvCGZEUI.js → composition-sources-C2VNX6tF.js} +1 -1
- package/dist/{composition-sources-BQNxXQZG.mjs → composition-sources-SYtYuAgh.mjs} +1 -1
- package/dist/{constants-CR4If_lP.mjs → constants-CKuWoWvc.mjs} +1 -1
- package/dist/{constants-CCSJGnjs.js → constants-RI-hGP5S.js} +1 -1
- package/dist/device-ca-BWfdDB3k.js +462 -0
- package/dist/device-ca-R7DKKZL6.mjs +355 -0
- package/dist/{dist-BQG3QFg7.js → dist-Bgy_AmwX.js} +554 -0
- package/dist/{dist-DHSJV4Qc.mjs → dist-CSSfTM18.mjs} +507 -1
- package/dist/file-host-worker.js +1 -1
- package/dist/file-host-worker.mjs +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.js +59 -12
- package/dist/index.mjs +10 -9
- package/dist/kernel/index.d.ts +2 -2
- package/dist/kernel/moleculer/mesh-mtls/index.d.ts +4 -1
- package/dist/kernel/moleculer/mesh-mtls/mesh-certificate-registry.d.ts +91 -0
- package/dist/kernel/moleculer/mesh-mtls/mesh-enroll.d.ts +4 -0
- package/dist/kernel/moleculer/mesh-mtls/mesh-peer-transport.d.ts +23 -0
- package/dist/kernel/moleculer/mesh-mtls/mesh-renewal.d.ts +126 -0
- package/dist/kernel/moleculer/mesh-mtls/mesh-tls-gate.d.ts +40 -0
- package/dist/kernel/moleculer/mesh-mtls/tls-tcp-transporter.d.ts +15 -0
- package/dist/{restore-archive-DH1Pxq_1.mjs → restore-archive-AIwMHO5_.mjs} +1 -1
- package/dist/{restore-archive-Dccm8lkA.js → restore-archive-BYUEMsog.js} +1 -1
- package/dist/{retired-settings-keys-BfGaSeQI.js → retired-settings-keys-BTPkRLTS.js} +1 -1
- package/dist/{retired-settings-keys-CPz7zpza.mjs → retired-settings-keys-H0rYXT27.mjs} +1 -1
- package/dist/tls/agent-https-names.d.ts +27 -0
- package/dist/tls/agent-https.d.ts +111 -0
- package/dist/tls/cert-issue.d.ts +7 -0
- package/dist/tls/index.d.ts +3 -0
- package/dist/{tls-BgImdxCO.js → tls-C5IYg3V5.js} +406 -0
- package/dist/{tls-CaOLZ09W.mjs → tls-CpcGkeJV.mjs} +314 -4
- package/dist/{wal-maintenance-DItkuOvu.js → wal-maintenance-BSGsxKW7.js} +0 -0
- package/dist/{wal-maintenance-Drqhcjit.mjs → wal-maintenance-DW0FfGV7.mjs} +0 -0
- package/package.json +4 -1
- package/dist/jwt-signing-key-BZm055IP.js +0 -174
- package/dist/jwt-signing-key-DL-qk1SM.mjs +0 -127
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
//#region src/tls/agent-https-names.ts
|
|
2
|
+
/**
|
|
3
|
+
* @durable class=config owner=system-config
|
|
4
|
+
* write="the hub records the names it signed into an agent's HTTPS leaf, replacing that
|
|
5
|
+
* node's previous row"
|
|
6
|
+
* retention="one row per node, replaced on every issue; a forgotten node's row stays until
|
|
7
|
+
* the node is issued again or an operator clears it (it only blocks reuse of its names)"
|
|
8
|
+
*/
|
|
9
|
+
var AGENT_HTTPS_NAMES_COLLECTION = "agent_https_names";
|
|
10
|
+
var AGENT_HTTPS_NAMES_COLUMNS = [
|
|
11
|
+
{
|
|
12
|
+
name: "id",
|
|
13
|
+
type: "TEXT",
|
|
14
|
+
primaryKey: true,
|
|
15
|
+
notNull: true
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
name: "names",
|
|
19
|
+
type: "JSON",
|
|
20
|
+
notNull: true
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
name: "issuedAt",
|
|
24
|
+
type: "INTEGER",
|
|
25
|
+
notNull: true
|
|
26
|
+
}
|
|
27
|
+
];
|
|
28
|
+
var AgentHttpsNames = class {
|
|
29
|
+
store;
|
|
30
|
+
byNode = /* @__PURE__ */ new Map();
|
|
31
|
+
constructor(store) {
|
|
32
|
+
this.store = store;
|
|
33
|
+
}
|
|
34
|
+
async load() {
|
|
35
|
+
const rows = await this.store.query.query({ collection: AGENT_HTTPS_NAMES_COLLECTION });
|
|
36
|
+
for (const row of rows) {
|
|
37
|
+
const names = row.data["names"];
|
|
38
|
+
if (Array.isArray(names)) this.byNode.set(row.id, names.filter((n) => typeof n === "string").map((n) => n.toLowerCase()));
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/** Every name certified to a node other than `nodeId`. */
|
|
42
|
+
takenByOthers(nodeId) {
|
|
43
|
+
const out = /* @__PURE__ */ new Set();
|
|
44
|
+
for (const [id, names] of this.byNode) {
|
|
45
|
+
if (id === nodeId) continue;
|
|
46
|
+
for (const n of names) out.add(n);
|
|
47
|
+
}
|
|
48
|
+
return out;
|
|
49
|
+
}
|
|
50
|
+
/** Free every name certified to a node that was forgotten. */
|
|
51
|
+
async release(nodeId) {
|
|
52
|
+
if (!this.byNode.delete(nodeId)) return false;
|
|
53
|
+
await this.store.delete.mutate({
|
|
54
|
+
collection: AGENT_HTTPS_NAMES_COLLECTION,
|
|
55
|
+
key: nodeId
|
|
56
|
+
});
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
async record(nodeId, names) {
|
|
60
|
+
const lowered = names.map((n) => n.toLowerCase());
|
|
61
|
+
const existed = this.byNode.has(nodeId);
|
|
62
|
+
this.byNode.set(nodeId, lowered);
|
|
63
|
+
const data = {
|
|
64
|
+
names: lowered,
|
|
65
|
+
issuedAt: Date.now()
|
|
66
|
+
};
|
|
67
|
+
if (existed) await this.store.update.mutate({
|
|
68
|
+
collection: AGENT_HTTPS_NAMES_COLLECTION,
|
|
69
|
+
id: nodeId,
|
|
70
|
+
data
|
|
71
|
+
});
|
|
72
|
+
else await this.store.insert.mutate({
|
|
73
|
+
collection: AGENT_HTTPS_NAMES_COLLECTION,
|
|
74
|
+
record: {
|
|
75
|
+
id: nodeId,
|
|
76
|
+
data
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
var SLOT = Symbol.for("camstack.agent-https.names");
|
|
82
|
+
function setProcessAgentHttpsNames(names) {
|
|
83
|
+
if (names === null) Reflect.deleteProperty(globalThis, SLOT);
|
|
84
|
+
else Reflect.set(globalThis, SLOT, names);
|
|
85
|
+
}
|
|
86
|
+
function getProcessAgentHttpsNames() {
|
|
87
|
+
const value = Reflect.get(globalThis, SLOT);
|
|
88
|
+
return value !== null && typeof value === "object" && typeof Reflect.get(value, "takenByOthers") === "function" ? value : null;
|
|
89
|
+
}
|
|
90
|
+
//#endregion
|
|
91
|
+
Object.defineProperty(exports, "AGENT_HTTPS_NAMES_COLLECTION", {
|
|
92
|
+
enumerable: true,
|
|
93
|
+
get: function() {
|
|
94
|
+
return AGENT_HTTPS_NAMES_COLLECTION;
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
Object.defineProperty(exports, "AGENT_HTTPS_NAMES_COLUMNS", {
|
|
98
|
+
enumerable: true,
|
|
99
|
+
get: function() {
|
|
100
|
+
return AGENT_HTTPS_NAMES_COLUMNS;
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
Object.defineProperty(exports, "AgentHttpsNames", {
|
|
104
|
+
enumerable: true,
|
|
105
|
+
get: function() {
|
|
106
|
+
return AgentHttpsNames;
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
Object.defineProperty(exports, "getProcessAgentHttpsNames", {
|
|
110
|
+
enumerable: true,
|
|
111
|
+
get: function() {
|
|
112
|
+
return getProcessAgentHttpsNames;
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
Object.defineProperty(exports, "setProcessAgentHttpsNames", {
|
|
116
|
+
enumerable: true,
|
|
117
|
+
get: function() {
|
|
118
|
+
return setProcessAgentHttpsNames;
|
|
119
|
+
}
|
|
120
|
+
});
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
//#region src/tls/agent-https-names.ts
|
|
2
|
+
/**
|
|
3
|
+
* @durable class=config owner=system-config
|
|
4
|
+
* write="the hub records the names it signed into an agent's HTTPS leaf, replacing that
|
|
5
|
+
* node's previous row"
|
|
6
|
+
* retention="one row per node, replaced on every issue; a forgotten node's row stays until
|
|
7
|
+
* the node is issued again or an operator clears it (it only blocks reuse of its names)"
|
|
8
|
+
*/
|
|
9
|
+
var AGENT_HTTPS_NAMES_COLLECTION = "agent_https_names";
|
|
10
|
+
var AGENT_HTTPS_NAMES_COLUMNS = [
|
|
11
|
+
{
|
|
12
|
+
name: "id",
|
|
13
|
+
type: "TEXT",
|
|
14
|
+
primaryKey: true,
|
|
15
|
+
notNull: true
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
name: "names",
|
|
19
|
+
type: "JSON",
|
|
20
|
+
notNull: true
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
name: "issuedAt",
|
|
24
|
+
type: "INTEGER",
|
|
25
|
+
notNull: true
|
|
26
|
+
}
|
|
27
|
+
];
|
|
28
|
+
var AgentHttpsNames = class {
|
|
29
|
+
store;
|
|
30
|
+
byNode = /* @__PURE__ */ new Map();
|
|
31
|
+
constructor(store) {
|
|
32
|
+
this.store = store;
|
|
33
|
+
}
|
|
34
|
+
async load() {
|
|
35
|
+
const rows = await this.store.query.query({ collection: AGENT_HTTPS_NAMES_COLLECTION });
|
|
36
|
+
for (const row of rows) {
|
|
37
|
+
const names = row.data["names"];
|
|
38
|
+
if (Array.isArray(names)) this.byNode.set(row.id, names.filter((n) => typeof n === "string").map((n) => n.toLowerCase()));
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/** Every name certified to a node other than `nodeId`. */
|
|
42
|
+
takenByOthers(nodeId) {
|
|
43
|
+
const out = /* @__PURE__ */ new Set();
|
|
44
|
+
for (const [id, names] of this.byNode) {
|
|
45
|
+
if (id === nodeId) continue;
|
|
46
|
+
for (const n of names) out.add(n);
|
|
47
|
+
}
|
|
48
|
+
return out;
|
|
49
|
+
}
|
|
50
|
+
/** Free every name certified to a node that was forgotten. */
|
|
51
|
+
async release(nodeId) {
|
|
52
|
+
if (!this.byNode.delete(nodeId)) return false;
|
|
53
|
+
await this.store.delete.mutate({
|
|
54
|
+
collection: AGENT_HTTPS_NAMES_COLLECTION,
|
|
55
|
+
key: nodeId
|
|
56
|
+
});
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
async record(nodeId, names) {
|
|
60
|
+
const lowered = names.map((n) => n.toLowerCase());
|
|
61
|
+
const existed = this.byNode.has(nodeId);
|
|
62
|
+
this.byNode.set(nodeId, lowered);
|
|
63
|
+
const data = {
|
|
64
|
+
names: lowered,
|
|
65
|
+
issuedAt: Date.now()
|
|
66
|
+
};
|
|
67
|
+
if (existed) await this.store.update.mutate({
|
|
68
|
+
collection: AGENT_HTTPS_NAMES_COLLECTION,
|
|
69
|
+
id: nodeId,
|
|
70
|
+
data
|
|
71
|
+
});
|
|
72
|
+
else await this.store.insert.mutate({
|
|
73
|
+
collection: AGENT_HTTPS_NAMES_COLLECTION,
|
|
74
|
+
record: {
|
|
75
|
+
id: nodeId,
|
|
76
|
+
data
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
var SLOT = Symbol.for("camstack.agent-https.names");
|
|
82
|
+
function setProcessAgentHttpsNames(names) {
|
|
83
|
+
if (names === null) Reflect.deleteProperty(globalThis, SLOT);
|
|
84
|
+
else Reflect.set(globalThis, SLOT, names);
|
|
85
|
+
}
|
|
86
|
+
function getProcessAgentHttpsNames() {
|
|
87
|
+
const value = Reflect.get(globalThis, SLOT);
|
|
88
|
+
return value !== null && typeof value === "object" && typeof Reflect.get(value, "takenByOthers") === "function" ? value : null;
|
|
89
|
+
}
|
|
90
|
+
//#endregion
|
|
91
|
+
export { setProcessAgentHttpsNames as a, getProcessAgentHttpsNames as i, AGENT_HTTPS_NAMES_COLUMNS as n, AgentHttpsNames as r, AGENT_HTTPS_NAMES_COLLECTION as t };
|
|
@@ -7,7 +7,7 @@ import type { CollectionColumn, IScopedLogger, SettingsStoreClient } from "@cams
|
|
|
7
7
|
*/
|
|
8
8
|
export declare const AUTH_AUDIT_COLLECTION = "auth_audit";
|
|
9
9
|
export declare const AUTH_AUDIT_COLUMNS: readonly CollectionColumn[];
|
|
10
|
-
export declare const AUTH_AUDIT_ACTIONS: readonly ["second-factor-skipped", "admin-token-created", "admin-token-used", "admin-token-revoked", "credentials-revoked", "agent-addon-update", "agent-addon-update-refused"];
|
|
10
|
+
export declare const AUTH_AUDIT_ACTIONS: readonly ["second-factor-skipped", "admin-token-created", "admin-token-used", "admin-token-revoked", "credentials-revoked", "agent-addon-update", "agent-addon-update-refused", "device-credential-enrolled", "device-credential-revoked", "device-credential-login", "device-credential-login-refused"];
|
|
11
11
|
export type AuthAuditAction = (typeof AUTH_AUDIT_ACTIONS)[number];
|
|
12
12
|
export interface AuthAuditEvent {
|
|
13
13
|
readonly action: AuthAuditAction;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Loads the device CA from the keychain + settings, and creates it LAZILY on
|
|
3
|
+
* the first enrollment (D855). Never rotated on boot, never silently replaced.
|
|
4
|
+
*
|
|
5
|
+
* The two halves of the CA and what each combination means:
|
|
6
|
+
*
|
|
7
|
+
* key cert state
|
|
8
|
+
* ---- ---- -----------------------------------------------------------
|
|
9
|
+
* no no `absent` - no device was ever enrolled: `ensure()` creates it
|
|
10
|
+
* yes yes `ready`
|
|
11
|
+
* yes no recovered - a creation that died between its two writes:
|
|
12
|
+
* the certificate is rebuilt from the SAME key
|
|
13
|
+
* (same subject, same public key, so every leaf
|
|
14
|
+
* already issued still verifies) and written
|
|
15
|
+
* no yes `unavailable` - the key is GONE (keychain reset, restore). A
|
|
16
|
+
* new CA would orphan every enrolled device
|
|
17
|
+
* without a word, so this is an explicit error:
|
|
18
|
+
* logged, shown by `getDeviceCaStatus`, and
|
|
19
|
+
* `ensure()` refuses. The operator restores the
|
|
20
|
+
* key (or knowingly removes `cluster.deviceCa`).
|
|
21
|
+
*
|
|
22
|
+
* Creation order is KEY first, then certificate. If the certificate write
|
|
23
|
+
* fails after the key was stored, the next `lookup()` finds (yes, no) and
|
|
24
|
+
* completes the creation deterministically - there is no half-state that needs
|
|
25
|
+
* a human.
|
|
26
|
+
*/
|
|
27
|
+
import type { IScopedLogger, SecretsAccess } from "@camstack/types";
|
|
28
|
+
import { type DeviceCaSigner } from "./device-ca.js";
|
|
29
|
+
export interface DeviceCaStoreDeps {
|
|
30
|
+
/** Keychain handle for owner `system`; `null` when this process has no keychain door. */
|
|
31
|
+
readonly secrets: SecretsAccess | null;
|
|
32
|
+
readonly readSection: (section: string) => Promise<Record<string, unknown>>;
|
|
33
|
+
readonly writeSection: (section: string, patch: Record<string, unknown>) => Promise<void>;
|
|
34
|
+
readonly logger?: IScopedLogger;
|
|
35
|
+
}
|
|
36
|
+
export type DeviceCaLookup = {
|
|
37
|
+
readonly state: "ready";
|
|
38
|
+
readonly ca: DeviceCaSigner;
|
|
39
|
+
} | {
|
|
40
|
+
readonly state: "absent";
|
|
41
|
+
} | {
|
|
42
|
+
readonly state: "unavailable";
|
|
43
|
+
readonly reason: string;
|
|
44
|
+
};
|
|
45
|
+
export declare class DeviceCaStore {
|
|
46
|
+
private readonly deps;
|
|
47
|
+
private loaded;
|
|
48
|
+
private creating;
|
|
49
|
+
private lastReported;
|
|
50
|
+
constructor(deps: DeviceCaStoreDeps);
|
|
51
|
+
/** The CA when one exists (completing an interrupted creation). Never creates a NEW one. */
|
|
52
|
+
lookup(): Promise<DeviceCaLookup>;
|
|
53
|
+
/** The CA, created now if neither half exists. Throws on any inconsistent or unreadable state. */
|
|
54
|
+
ensure(): Promise<DeviceCaSigner>;
|
|
55
|
+
private report;
|
|
56
|
+
private create;
|
|
57
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Device CA (D855) — the THIRD certificate authority, distinct from the HTTPS
|
|
3
|
+
* local CA (`tls/cert-issue.ts`, RSA) and the mesh CA
|
|
4
|
+
* (`kernel/moleculer/mesh-mtls/mesh-ca.ts`). EC P-256, key in the keychain
|
|
5
|
+
* (owner `system`), certificate persisted like `cluster.meshCa`.
|
|
6
|
+
*
|
|
7
|
+
* It signs ONE kind of leaf: a device credential. The device generated the key
|
|
8
|
+
* pair; the hub only ever sees the public half (an SPKI). Same library and
|
|
9
|
+
* WebCrypto path as the other two CAs, same pinned `@peculiar/x509`.
|
|
10
|
+
*/
|
|
11
|
+
import type { webcrypto as WebCrypto } from "node:crypto";
|
|
12
|
+
import { X509Certificate as PeculiarCertificate } from "@peculiar/x509";
|
|
13
|
+
/** `secret://system/global/deviceCaKey` — EC P-256 PKCS#8 PEM of the device CA. */
|
|
14
|
+
export declare const DEVICE_CA_KEY_REF: string;
|
|
15
|
+
/** Settings section / field holding the public CA certificate PEM (beside `cluster.meshCa`). */
|
|
16
|
+
export declare const DEVICE_CA_SETTINGS_SECTION = "cluster";
|
|
17
|
+
export declare const DEVICE_CA_FIELD = "deviceCa";
|
|
18
|
+
export declare const DEVICE_CA_COMMON_NAME = "CamStack Device CA";
|
|
19
|
+
export declare const DEVICE_CA_VALIDITY_DAYS = 3650;
|
|
20
|
+
export interface DeviceCaSigner {
|
|
21
|
+
readonly certPem: string;
|
|
22
|
+
readonly keyPem: string;
|
|
23
|
+
readonly cert: PeculiarCertificate;
|
|
24
|
+
readonly privateKey: WebCrypto.CryptoKey;
|
|
25
|
+
}
|
|
26
|
+
export interface DeviceLeafMaterial {
|
|
27
|
+
readonly certPem: string;
|
|
28
|
+
readonly serialNumber: string;
|
|
29
|
+
readonly notBefore: Date;
|
|
30
|
+
readonly notAfter: Date;
|
|
31
|
+
/** SHA-256 of the DER, colon-hex uppercase. */
|
|
32
|
+
readonly fingerprint: string;
|
|
33
|
+
}
|
|
34
|
+
export declare function generateDeviceCa(now?: Date): Promise<DeviceCaSigner>;
|
|
35
|
+
export declare function loadDeviceCa(certPem: string, keyPem: string): Promise<DeviceCaSigner>;
|
|
36
|
+
/**
|
|
37
|
+
* A new self-signed certificate for the SAME key pair as `keyPem` (same
|
|
38
|
+
* subject, so leaves already issued still verify): completes a CA creation that
|
|
39
|
+
* died between its key write and its certificate write.
|
|
40
|
+
*/
|
|
41
|
+
export declare function rebuildDeviceCaCertificate(keyPem: string): Promise<DeviceCaSigner>;
|
|
42
|
+
/** Import (and thereby validate) a device public key: SPKI DER, ECDSA P-256. */
|
|
43
|
+
export declare function importDevicePublicKey(spki: Uint8Array): Promise<WebCrypto.CryptoKey>;
|
|
44
|
+
export interface IssueDeviceLeafInput {
|
|
45
|
+
readonly credentialId: string;
|
|
46
|
+
readonly userId: string;
|
|
47
|
+
readonly publicKeySpki: Uint8Array;
|
|
48
|
+
readonly now?: Date;
|
|
49
|
+
readonly validDays?: number;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Sign a device leaf: CN = credential id; SAN URIs `camstack:credential:<id>`
|
|
53
|
+
* and `camstack:user:<userId>`; EKU clientAuth; 365 days by default.
|
|
54
|
+
*/
|
|
55
|
+
export declare function issueDeviceLeaf(ca: DeviceCaSigner, input: IssueDeviceLeafInput): Promise<DeviceLeafMaterial>;
|
|
56
|
+
export type DeviceLeafRefusal = "malformed-certificate" | "not-issued-by-device-ca" | "not-yet-valid" | "expired" | "missing-credential-san" | "missing-client-auth";
|
|
57
|
+
export type DeviceLeafVerdict = {
|
|
58
|
+
readonly ok: true;
|
|
59
|
+
readonly credentialId: string;
|
|
60
|
+
readonly userId: string | null;
|
|
61
|
+
readonly publicKeySpki: Buffer;
|
|
62
|
+
readonly serialNumber: string;
|
|
63
|
+
readonly fingerprint: string;
|
|
64
|
+
readonly notAfter: Date;
|
|
65
|
+
} | {
|
|
66
|
+
readonly ok: false;
|
|
67
|
+
readonly reason: DeviceLeafRefusal;
|
|
68
|
+
};
|
|
69
|
+
/** Chain (signature by the device CA), validity window, EKU and identity of a presented leaf. */
|
|
70
|
+
export declare function verifyDeviceLeaf(caCertPem: string, leafPem: string, now?: Date): Promise<DeviceLeafVerdict>;
|
|
71
|
+
/**
|
|
72
|
+
* ECDSA P-256 / SHA-256 over `message`. Accepts the raw `r||s` form WebCrypto
|
|
73
|
+
* and Android emit (64 bytes) and the DER form the Secure Enclave emits.
|
|
74
|
+
*/
|
|
75
|
+
export declare function verifyDeviceSignature(publicKeySpki: Uint8Array, message: string, signatureBase64Url: string): boolean;
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import type { CollectionColumn, IScopedLogger, SettingsStoreClient } from "@camstack/types";
|
|
2
|
+
import { type DeviceCredential, type DevicePlatform, type DeviceSignaturePurpose } from "@camstack/types";
|
|
3
|
+
import type { DeviceCaStore } from "./device-ca-store.js";
|
|
4
|
+
/**
|
|
5
|
+
* @durable class=config owner=local-auth
|
|
6
|
+
* write="an enrollment completes (QR or self) inserts a row; a renewal rewrites
|
|
7
|
+
* fingerprint/expiry; every accepted certificate login rewrites lastUsedAt/From;
|
|
8
|
+
* an admin revokes or renames"
|
|
9
|
+
* retention="a revoked or long-expired row is kept 90 days for the audit question
|
|
10
|
+
* 'which device was that', then the daily reap deletes it; an active row is
|
|
11
|
+
* never deleted"
|
|
12
|
+
*/
|
|
13
|
+
export declare const DEVICE_CREDENTIALS_COLLECTION = "device_credentials";
|
|
14
|
+
export declare const DEVICE_CREDENTIALS_COLUMNS: readonly CollectionColumn[];
|
|
15
|
+
export type EnrolledVia = "qr" | "self";
|
|
16
|
+
export interface DeviceCredentialRecord {
|
|
17
|
+
readonly id: string;
|
|
18
|
+
readonly userId: string;
|
|
19
|
+
readonly deviceName: string;
|
|
20
|
+
readonly platform: DevicePlatform;
|
|
21
|
+
readonly enrolledVia: EnrolledVia;
|
|
22
|
+
readonly fingerprint: string;
|
|
23
|
+
readonly serial: string;
|
|
24
|
+
/** The fingerprint the leaf had before the last renewal, accepted until {@link PREVIOUS_LEAF_GRACE_MS} after `renewedAt`. */
|
|
25
|
+
readonly previousFingerprint: string | null;
|
|
26
|
+
readonly renewedAt: number | null;
|
|
27
|
+
/** SPKI DER, base64url. */
|
|
28
|
+
readonly publicKey: string;
|
|
29
|
+
readonly issuedAt: number;
|
|
30
|
+
readonly expiresAt: number;
|
|
31
|
+
readonly lastUsedAt: number | null;
|
|
32
|
+
readonly lastUsedFrom: string | null;
|
|
33
|
+
readonly revokedAt: number | null;
|
|
34
|
+
readonly revokedBy: string | null;
|
|
35
|
+
}
|
|
36
|
+
export type DeviceCredentialState = "active" | "expired" | "revoked" | "unknown";
|
|
37
|
+
export type DeviceCheckRefusal = "unknown-or-used-code" | "expired-code" | "user-gone" | "ca-unavailable" | "bad-public-key" | "self-enrollment-disabled" | "unknown-nonce" | "expired-nonce" | "no-device-ca" | "malformed-certificate" | "not-issued-by-device-ca" | "not-yet-valid" | "expired" | "missing-credential-san" | "missing-client-auth" | "unknown-credential" | "revoked" | "key-mismatch" | "bad-signature" | "user-mismatch" | "superseded";
|
|
38
|
+
export type Refused = {
|
|
39
|
+
readonly ok: false;
|
|
40
|
+
readonly reason: DeviceCheckRefusal;
|
|
41
|
+
};
|
|
42
|
+
export interface IssuedDeviceCertificate {
|
|
43
|
+
readonly ok: true;
|
|
44
|
+
readonly credentialId: string;
|
|
45
|
+
readonly userId: string;
|
|
46
|
+
readonly certPem: string;
|
|
47
|
+
readonly chainPem: string;
|
|
48
|
+
readonly notAfter: number;
|
|
49
|
+
}
|
|
50
|
+
export interface AuthenticatedDevice {
|
|
51
|
+
readonly ok: true;
|
|
52
|
+
readonly credential: DeviceCredentialRecord;
|
|
53
|
+
}
|
|
54
|
+
export interface DeviceNonce {
|
|
55
|
+
readonly nonce: string;
|
|
56
|
+
readonly expiresAt: number;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* After a renewal the PREVIOUS leaf still logs in for this long, so a device
|
|
60
|
+
* that crashed between the hub's answer and its own write is not locked out;
|
|
61
|
+
* after it, the old leaf is `superseded` (not definitive: the app holds the
|
|
62
|
+
* newest certificate, so seeing it means the device lost the new one).
|
|
63
|
+
*/
|
|
64
|
+
export declare const PREVIOUS_LEAF_GRACE_MS: number;
|
|
65
|
+
export interface DeviceCredentialManagerDeps {
|
|
66
|
+
readonly ca: Pick<DeviceCaStore, "lookup" | "ensure">;
|
|
67
|
+
/** The user a credential names; `null` when gone. */
|
|
68
|
+
readonly findUser: (userId: string) => Promise<{
|
|
69
|
+
readonly username: string;
|
|
70
|
+
} | null>;
|
|
71
|
+
/** Hub-wide switch for `enrollSelf`; absent = allowed. */
|
|
72
|
+
readonly selfEnrollmentAllowed: () => Promise<boolean>;
|
|
73
|
+
readonly now?: () => number;
|
|
74
|
+
readonly logger?: IScopedLogger;
|
|
75
|
+
}
|
|
76
|
+
export declare class DeviceCredentialManager {
|
|
77
|
+
private readonly store;
|
|
78
|
+
private readonly deps;
|
|
79
|
+
private readonly records;
|
|
80
|
+
private readonly pendingEnrollments;
|
|
81
|
+
private readonly usedCodes;
|
|
82
|
+
private readonly nonces;
|
|
83
|
+
private readonly now;
|
|
84
|
+
constructor(store: SettingsStoreClient, deps: DeviceCredentialManagerDeps);
|
|
85
|
+
load(): Promise<void>;
|
|
86
|
+
/** A one-shot code for `userId` (admin path). Returned once; only its hash is kept. */
|
|
87
|
+
startEnrollment(input: {
|
|
88
|
+
readonly userId: string;
|
|
89
|
+
readonly deviceName: string;
|
|
90
|
+
readonly createdBy: string;
|
|
91
|
+
}): {
|
|
92
|
+
readonly code: string;
|
|
93
|
+
readonly expiresAt: number;
|
|
94
|
+
};
|
|
95
|
+
/** Redeem a code with the device's public key (public procedure). */
|
|
96
|
+
completeEnrollment(input: {
|
|
97
|
+
readonly code: string;
|
|
98
|
+
readonly publicKeySpki: string;
|
|
99
|
+
readonly deviceName?: string;
|
|
100
|
+
readonly platform: DevicePlatform;
|
|
101
|
+
}): Promise<(IssuedDeviceCertificate & {
|
|
102
|
+
readonly username: string;
|
|
103
|
+
}) | Refused>;
|
|
104
|
+
/** A signed-in user's own device (the caller has already proved a step-up). */
|
|
105
|
+
enrollSelf(input: {
|
|
106
|
+
readonly userId: string;
|
|
107
|
+
readonly publicKeySpki: string;
|
|
108
|
+
readonly deviceName: string;
|
|
109
|
+
readonly platform: DevicePlatform;
|
|
110
|
+
}): Promise<(IssuedDeviceCertificate & {
|
|
111
|
+
readonly username: string;
|
|
112
|
+
}) | Refused>;
|
|
113
|
+
private issueNew;
|
|
114
|
+
private issued;
|
|
115
|
+
/** A single-use nonce bound to `origin`. */
|
|
116
|
+
issueNonce(origin: string, partition?: string): DeviceNonce;
|
|
117
|
+
/**
|
|
118
|
+
* Check a presented certificate + signature. Consumes the nonce on the first
|
|
119
|
+
* attempt. On success the credential's lastUsed is stamped.
|
|
120
|
+
*/
|
|
121
|
+
authenticate(input: {
|
|
122
|
+
readonly certPem: string;
|
|
123
|
+
readonly nonce: string;
|
|
124
|
+
readonly signature: string;
|
|
125
|
+
readonly purpose: DeviceSignaturePurpose;
|
|
126
|
+
readonly from?: string;
|
|
127
|
+
}): Promise<AuthenticatedDevice | Refused>;
|
|
128
|
+
/** A new leaf for the SAME public key, after {@link authenticate} accepted a `renew` proof. */
|
|
129
|
+
renew(credential: DeviceCredentialRecord): Promise<IssuedDeviceCertificate | Refused>;
|
|
130
|
+
/** Synchronous: the hub asks on every refresh of a credential-bound session. */
|
|
131
|
+
check(credentialId: string): DeviceCredentialState;
|
|
132
|
+
get(credentialId: string): DeviceCredentialRecord | undefined;
|
|
133
|
+
list(input: {
|
|
134
|
+
readonly userId?: string;
|
|
135
|
+
readonly includeRevoked: boolean;
|
|
136
|
+
}): readonly DeviceCredentialRecord[];
|
|
137
|
+
/**
|
|
138
|
+
* Delete a REVOKED row (admin clean-up). `live` refuses a credential that is
|
|
139
|
+
* not revoked — a live credential is revoked first, so a delete can never be
|
|
140
|
+
* the way a device silently stops working.
|
|
141
|
+
*/
|
|
142
|
+
delete(credentialId: string): Promise<"deleted" | "live" | "unknown">;
|
|
143
|
+
/** Revoke; `false` when unknown or already revoked. `revokedBy`: see `formatDeviceRevoker`. */
|
|
144
|
+
revoke(credentialId: string, revokedBy: string): Promise<boolean>;
|
|
145
|
+
rename(credentialId: string, deviceName: string): Promise<boolean>;
|
|
146
|
+
/** Revoke every credential of a user (account deleted). Returns their ids. */
|
|
147
|
+
revokeAllForUser(userId: string, revokedBy: string): Promise<readonly string[]>;
|
|
148
|
+
toSummary(record: DeviceCredentialRecord, username: string | null): DeviceCredential;
|
|
149
|
+
/** Delete rows revoked or expired more than 90 days ago. */
|
|
150
|
+
reap(): Promise<number>;
|
|
151
|
+
private save;
|
|
152
|
+
private refuse;
|
|
153
|
+
private prune;
|
|
154
|
+
}
|
|
@@ -20,9 +20,9 @@ export declare const LOGIN_SESSION_CUTOFFS_COLLECTION = "login_session_cutoffs";
|
|
|
20
20
|
export declare const LOGIN_SESSIONS_COLUMNS: readonly CollectionColumn[];
|
|
21
21
|
export declare const LOGIN_SESSION_CUTOFFS_COLUMNS: readonly CollectionColumn[];
|
|
22
22
|
/** How a session began — shown on the sessions list. */
|
|
23
|
-
export type LoginSessionMethod = "password" | "passkey" | "sso" | "handoff" | "exchange";
|
|
23
|
+
export type LoginSessionMethod = "password" | "passkey" | "sso" | "handoff" | "exchange" | "device-certificate";
|
|
24
24
|
/** Why a session ended. */
|
|
25
|
-
export type LoginSessionRevocation = "logout" | "logout-everywhere" | "password-changed" | "password-reset" | "user-deleted" | "refresh-reuse" | "revoked-by-user" | "revoked-by-admin";
|
|
25
|
+
export type LoginSessionRevocation = "logout" | "logout-everywhere" | "password-changed" | "password-reset" | "user-deleted" | "refresh-reuse" | "revoked-by-user" | "revoked-by-admin" | "credential-revoked" | "credential-expired";
|
|
26
26
|
export interface LoginSessionRecord {
|
|
27
27
|
readonly id: string;
|
|
28
28
|
readonly userId: string;
|
|
@@ -38,6 +38,8 @@ export interface LoginSessionRecord {
|
|
|
38
38
|
readonly rotatedAt: number | null;
|
|
39
39
|
readonly client: string | null;
|
|
40
40
|
readonly ip: string | null;
|
|
41
|
+
/** The device credential (D855) that opened this session; `null` for every other login. */
|
|
42
|
+
readonly credentialId: string | null;
|
|
41
43
|
}
|
|
42
44
|
/** A session as the UI sees it — no refresh hashes. */
|
|
43
45
|
export type LoginSessionSummary = Omit<LoginSessionRecord, "refreshHash" | "previousRefreshHash" | "rotatedAt">;
|
|
@@ -48,6 +50,8 @@ export interface CreateLoginSessionInput {
|
|
|
48
50
|
readonly method: LoginSessionMethod;
|
|
49
51
|
readonly client?: string;
|
|
50
52
|
readonly ip?: string;
|
|
53
|
+
/** Set by a device-certificate login; the refresh path re-checks it. */
|
|
54
|
+
readonly credentialId?: string;
|
|
51
55
|
}
|
|
52
56
|
export type LoginSessionCheck = {
|
|
53
57
|
readonly state: "active";
|
|
@@ -88,6 +92,12 @@ export declare class LoginSessionManager {
|
|
|
88
92
|
check(sessionId: string): LoginSessionCheck;
|
|
89
93
|
/** Epoch ms before which a pre-session (no `sid`) token of `userId` is refused; 0 = none. */
|
|
90
94
|
legacyCutoff(userId: string): number;
|
|
95
|
+
/**
|
|
96
|
+
* The session a refresh token belongs to, WITHOUT rotating it - so the
|
|
97
|
+
* refresh path can judge the session (its device credential) before it
|
|
98
|
+
* spends the token. `null` when the token names no session.
|
|
99
|
+
*/
|
|
100
|
+
peekByRefreshToken(refreshToken: string): LoginSessionRecord | null;
|
|
91
101
|
/** Swap a refresh token for a rotated one, sliding the session's expiry to now + `lifetimeMs`. */
|
|
92
102
|
refresh(refreshToken: string, lifetimeMs: number): Promise<LoginSessionRefresh>;
|
|
93
103
|
/** Renew an active session (legacy client): slide its expiry. `null` when it is not active. */
|
|
@@ -103,6 +113,8 @@ export declare class LoginSessionManager {
|
|
|
103
113
|
readonly exceptSessionId?: string;
|
|
104
114
|
readonly reason: LoginSessionRevocation;
|
|
105
115
|
}): Promise<number>;
|
|
116
|
+
/** End every live session opened by device credential `credentialId`. Returns how many. */
|
|
117
|
+
revokeByCredential(credentialId: string, reason?: LoginSessionRevocation): Promise<number>;
|
|
106
118
|
/** `userId`'s live sessions, newest first. */
|
|
107
119
|
listForUser(userId: string): readonly LoginSessionSummary[];
|
|
108
120
|
/** Every live session, newest first. */
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
*/
|
|
16
16
|
import type { AdminTokenRecord } from "./admin-token-manager.js";
|
|
17
17
|
import type { AuthAuditEvent, AuthAuditRecord } from "./auth-audit-log.js";
|
|
18
|
+
import type { DeviceCredentialManager } from "./device-credential-manager.js";
|
|
18
19
|
import type { LoginSessionManager } from "./login-session-manager.js";
|
|
19
20
|
/** Process-global, so every bundled copy of this module agrees on the key. */
|
|
20
21
|
export declare const USER_MANAGEMENT_INTERNAL: unique symbol;
|
|
@@ -26,7 +27,13 @@ export interface LinkExternalIdentityInput {
|
|
|
26
27
|
readonly label?: string;
|
|
27
28
|
}
|
|
28
29
|
/** The login-session table, as the hub uses it (`login-session-manager.ts`). */
|
|
29
|
-
export type LoginSessionsInternal = Pick<LoginSessionManager, "create" | "refresh" | "extend" | "check" | "legacyCutoff" | "revoke" | "revokeAllForUser" | "listForUser" | "listActive">;
|
|
30
|
+
export type LoginSessionsInternal = Pick<LoginSessionManager, "create" | "refresh" | "extend" | "check" | "legacyCutoff" | "revoke" | "revokeAllForUser" | "revokeByCredential" | "peekByRefreshToken" | "listForUser" | "listActive">;
|
|
31
|
+
/**
|
|
32
|
+
* Device credentials (D855), as the hub's public `auth` procedures use them.
|
|
33
|
+
* Hub-internal for the same reason as the rest: completing an enrollment or
|
|
34
|
+
* proving a certificate are proofs of possession, never cap calls.
|
|
35
|
+
*/
|
|
36
|
+
export type DeviceCredentialsInternal = Pick<DeviceCredentialManager, "completeEnrollment" | "enrollSelf" | "issueNonce" | "authenticate" | "renew" | "check" | "get" | "revoke" | "list">;
|
|
30
37
|
export interface SetPasswordInput {
|
|
31
38
|
readonly userId: string;
|
|
32
39
|
readonly newPassword: string;
|
|
@@ -63,6 +70,8 @@ export interface UserManagementInternal {
|
|
|
63
70
|
readonly oauthSessions: OauthSessionsInternal;
|
|
64
71
|
/** The authentication audit trail. */
|
|
65
72
|
readonly audit: AuthAuditInternal;
|
|
73
|
+
/** Device certificates: enrollment legs, challenge, proof, renewal, liveness check. */
|
|
74
|
+
readonly deviceCredentials: DeviceCredentialsInternal;
|
|
66
75
|
}
|
|
67
76
|
export interface ScopedTokensInternal {
|
|
68
77
|
revoke(id: string): Promise<boolean>;
|
|
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
const require_chunk = require("../../chunk-Cek0wNdY.js");
|
|
6
|
-
const require_dist = require("../../dist-
|
|
6
|
+
const require_dist = require("../../dist-Bgy_AmwX.js");
|
|
7
7
|
const require_settle_sources = require("../../settle-sources-Bhsy57y-.js");
|
|
8
8
|
let node_crypto = require("node:crypto");
|
|
9
9
|
let node_path = require("node:path");
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { J as addonPagesCapability, _r as BaseAddon, gr as errMsg, li as EventCategory } from "../../dist-CSSfTM18.mjs";
|
|
2
2
|
import { t as settleSourcesWithTimeout } from "../../settle-sources-CDtNC8ub.mjs";
|
|
3
3
|
import { createHash, randomUUID } from "node:crypto";
|
|
4
4
|
import * as path$1 from "node:path";
|
|
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
const require_chunk = require("../../chunk-Cek0wNdY.js");
|
|
6
|
-
const require_dist = require("../../dist-
|
|
6
|
+
const require_dist = require("../../dist-Bgy_AmwX.js");
|
|
7
7
|
const require_settle_sources = require("../../settle-sources-Bhsy57y-.js");
|
|
8
8
|
let node_crypto = require("node:crypto");
|
|
9
9
|
let node_path = require("node:path");
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { X as addonWidgetsCapability, _r as BaseAddon, gr as errMsg, li as EventCategory } from "../../dist-CSSfTM18.mjs";
|
|
2
2
|
import { t as settleSourcesWithTimeout } from "../../settle-sources-CDtNC8ub.mjs";
|
|
3
3
|
import { createHash, randomUUID } from "node:crypto";
|
|
4
4
|
import * as path$1 from "node:path";
|
|
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
require("../../chunk-Cek0wNdY.js");
|
|
6
|
-
const require_dist = require("../../dist-
|
|
6
|
+
const require_dist = require("../../dist-Bgy_AmwX.js");
|
|
7
7
|
//#region src/builtins/alerts/device-label.ts
|
|
8
8
|
/**
|
|
9
9
|
* Operator-facing device label for alert messages.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Nr as createEvent, Z as alertsCapability, _r as BaseAddon, gr as errMsg, li as EventCategory, n as AlertSchema } from "../../dist-CSSfTM18.mjs";
|
|
2
2
|
//#region src/builtins/alerts/device-label.ts
|
|
3
3
|
/**
|
|
4
4
|
* Operator-facing device label for alert messages.
|
|
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
require("../../chunk-Cek0wNdY.js");
|
|
6
|
-
const require_dist = require("../../dist-
|
|
6
|
+
const require_dist = require("../../dist-Bgy_AmwX.js");
|
|
7
7
|
//#region src/builtins/autotrack/autotrack-config.ts
|
|
8
8
|
var DEFAULT_AUTOTRACK_LOOP_CONFIG = {
|
|
9
9
|
deadZoneFraction: .5,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Cn as ptzAutotrackCapability, D as FIRST_LEVEL_MACRO_CLASSES, Hr as isEvent, _r as BaseAddon, et as bareAddonId, gr as errMsg, li as EventCategory } from "../../dist-CSSfTM18.mjs";
|
|
2
2
|
//#region src/builtins/autotrack/autotrack-config.ts
|
|
3
3
|
var DEFAULT_AUTOTRACK_LOOP_CONFIG = {
|
|
4
4
|
deadZoneFraction: .5,
|