@camstack/system 1.2.455 → 1.2.457

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (133) hide show
  1. package/dist/addon-runner.js +96 -10
  2. package/dist/addon-runner.mjs +96 -10
  3. package/dist/{agent-https-CLWrOO3o.mjs → agent-https-CK9Z4sSR.mjs} +32 -5
  4. package/dist/{agent-https-CCvlRQKV.js → agent-https-CoyOZ3pC.js} +37 -4
  5. package/dist/auth/auth-manager.d.ts +9 -0
  6. package/dist/auth/legacy-token-tables.d.ts +16 -0
  7. package/dist/auth/user-management-internal.d.ts +9 -28
  8. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +1 -1
  9. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +1 -1
  10. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +1 -1
  11. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +1 -1
  12. package/dist/builtins/alerts/alerts.addon.js +2 -2
  13. package/dist/builtins/alerts/alerts.addon.mjs +2 -2
  14. package/dist/builtins/autotrack/index.js +1 -1
  15. package/dist/builtins/autotrack/index.mjs +1 -1
  16. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +2 -2
  17. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +2 -2
  18. package/dist/builtins/calendars/calendars.addon.js +1 -1
  19. package/dist/builtins/calendars/calendars.addon.mjs +1 -1
  20. package/dist/builtins/camera-grid/index.js +1 -1
  21. package/dist/builtins/camera-grid/index.mjs +1 -1
  22. package/dist/builtins/composer/composer.addon.js +2 -2
  23. package/dist/builtins/composer/composer.addon.mjs +2 -2
  24. package/dist/builtins/console-logging/console-destination.d.ts +1 -2
  25. package/dist/builtins/console-logging/index.js +1 -4
  26. package/dist/builtins/console-logging/index.mjs +1 -4
  27. package/dist/builtins/core-blocks/core-blocks.addon.js +2 -2
  28. package/dist/builtins/core-blocks/core-blocks.addon.mjs +2 -2
  29. package/dist/builtins/device-history/capture/capture-engine.d.ts +6 -0
  30. package/dist/builtins/device-history/device-history.addon.js +441 -83
  31. package/dist/builtins/device-history/device-history.addon.mjs +441 -83
  32. package/dist/builtins/device-history/query/history-query.d.ts +35 -9
  33. package/dist/builtins/device-history/query/point-shape.d.ts +33 -0
  34. package/dist/builtins/device-history/retention/retention.d.ts +1 -1
  35. package/dist/builtins/device-history/rollup/rollup-engine.d.ts +2 -0
  36. package/dist/builtins/device-history/rollup/rollup-math.d.ts +6 -0
  37. package/dist/builtins/device-history/store/history-store.d.ts +7 -0
  38. package/dist/builtins/device-history/store/series-registry.d.ts +26 -1
  39. package/dist/builtins/device-manager/device-manager.addon.js +1 -1
  40. package/dist/builtins/device-manager/device-manager.addon.mjs +1 -1
  41. package/dist/builtins/hub-forwarder/hub-forwarder-destination.d.ts +1 -2
  42. package/dist/builtins/hub-forwarder/index.js +1 -4
  43. package/dist/builtins/hub-forwarder/index.mjs +1 -4
  44. package/dist/builtins/keychain/index.js +4 -4
  45. package/dist/builtins/keychain/index.mjs +4 -4
  46. package/dist/builtins/liveness-monitor/liveness-monitor.addon.js +1 -1
  47. package/dist/builtins/liveness-monitor/liveness-monitor.addon.mjs +1 -1
  48. package/dist/builtins/local-auth/auth-schema.d.ts +4 -7
  49. package/dist/builtins/local-auth/local-auth.addon.d.ts +7 -8
  50. package/dist/builtins/local-auth/local-auth.addon.js +1086 -1329
  51. package/dist/builtins/local-auth/local-auth.addon.mjs +1085 -1326
  52. package/dist/builtins/local-auth/preferences-record-kinds.d.ts +27 -0
  53. package/dist/builtins/local-auth/record-kinds.d.ts +4 -3
  54. package/dist/builtins/local-auth/user-preferences-store.d.ts +56 -0
  55. package/dist/builtins/local-network/local-network.addon.d.ts +8 -0
  56. package/dist/builtins/local-network/local-network.addon.js +30 -5
  57. package/dist/builtins/local-network/local-network.addon.mjs +30 -5
  58. package/dist/builtins/loki-logging/index.js +323 -7
  59. package/dist/builtins/loki-logging/index.mjs +323 -7
  60. package/dist/builtins/loki-logging/loki-destination.d.ts +20 -3
  61. package/dist/builtins/loki-logging/loki-read.d.ts +51 -0
  62. package/dist/builtins/native-metrics/native-metrics.addon.js +290 -112
  63. package/dist/builtins/native-metrics/native-metrics.addon.mjs +290 -112
  64. package/dist/builtins/native-metrics/node-metrics-history-recorder.d.ts +106 -35
  65. package/dist/builtins/platform-probe/index.js +1 -1
  66. package/dist/builtins/platform-probe/index.mjs +1 -1
  67. package/dist/builtins/record-entries/index.js +1 -1
  68. package/dist/builtins/record-entries/index.mjs +1 -1
  69. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
  70. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
  71. package/dist/builtins/snapshot/index.js +1 -1
  72. package/dist/builtins/snapshot/index.mjs +1 -1
  73. package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
  74. package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
  75. package/dist/builtins/sqlite-storage/index.js +1 -1
  76. package/dist/builtins/sqlite-storage/index.mjs +1 -1
  77. package/dist/builtins/sqlite-storage/records/records-engine-types.d.ts +1 -0
  78. package/dist/builtins/sqlite-storage/records/records-provider.d.ts +13 -0
  79. package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +52 -6
  80. package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +52 -6
  81. package/dist/builtins/storage-orchestrator/location-history-recorder.d.ts +76 -0
  82. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.d.ts +9 -0
  83. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +144 -1
  84. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +144 -1
  85. package/dist/builtins/system-config/system-config.addon.js +3 -3
  86. package/dist/builtins/system-config/system-config.addon.mjs +3 -3
  87. package/dist/builtins/winston-logging/index.js +165 -3
  88. package/dist/builtins/winston-logging/index.mjs +165 -3
  89. package/dist/builtins/winston-logging/log-file-cursor.d.ts +12 -0
  90. package/dist/builtins/winston-logging/log-file-index-store.d.ts +11 -0
  91. package/dist/builtins/winston-logging/log-file-index.d.ts +77 -0
  92. package/dist/builtins/winston-logging/log-file-read-client.d.ts +44 -0
  93. package/dist/builtins/winston-logging/log-file-read-protocol.d.ts +41 -0
  94. package/dist/builtins/winston-logging/log-file-read-worker.d.ts +1 -0
  95. package/dist/builtins/winston-logging/log-file-reader.d.ts +51 -0
  96. package/dist/builtins/winston-logging/winston-destination.d.ts +20 -2
  97. package/dist/{composition-sources-CSzD5_qX.mjs → composition-sources-B_jeZDRJ.mjs} +1 -1
  98. package/dist/{composition-sources-BxUkLNSo.js → composition-sources-qKyvMAvc.js} +1 -1
  99. package/dist/{constants-DruwCkfp.js → constants-B_X9cWEK.js} +1 -1
  100. package/dist/{constants-DOlYt1t1.mjs → constants-cxKcZRMi.mjs} +1 -1
  101. package/dist/{device-ca-uFbTyY_o.mjs → device-ca-BB6nHAL5.mjs} +1 -1
  102. package/dist/{device-ca-QPBgfqVY.js → device-ca-CVK3YcR_.js} +1 -1
  103. package/dist/{dist-DjQ8C-ku.js → dist-C89nqNnn.js} +721 -154
  104. package/dist/{dist-BpSENkRJ.mjs → dist-Dhux90yB.mjs} +668 -155
  105. package/dist/file-host-worker.js +1 -1
  106. package/dist/file-host-worker.mjs +1 -1
  107. package/dist/{hub-mdns-ciao-DlgkFJVY.mjs → hub-mdns-ciao-B8olZqqd.mjs} +20 -13
  108. package/dist/{hub-mdns-ciao-Bkrvgnma.js → hub-mdns-ciao-DB1Y9agt.js} +20 -13
  109. package/dist/index.d.ts +3 -4
  110. package/dist/index.js +145 -24
  111. package/dist/index.mjs +138 -22
  112. package/dist/kernel/config-schema.d.ts +5 -0
  113. package/dist/kernel/moleculer/accessory-prune-gate.d.ts +55 -0
  114. package/dist/log-file-read-protocol-B7M3X3bT.mjs +785 -0
  115. package/dist/log-file-read-protocol-D0WcLbIV.js +817 -0
  116. package/dist/log-file-read-worker.js +64 -0
  117. package/dist/log-file-read-worker.mjs +64 -0
  118. package/dist/log-read-filter-Bi066jwU.js +134 -0
  119. package/dist/log-read-filter-D7Ev3zm-.mjs +93 -0
  120. package/dist/logging/log-manager.d.ts +10 -6
  121. package/dist/logging/log-read-filter.d.ts +43 -0
  122. package/dist/{phase6-declare-gate-DrmTtz7F.js → phase6-declare-gate-BO-_jLh_.js} +1 -1
  123. package/dist/{phase6-declare-gate-JQUqgQgq.mjs → phase6-declare-gate-x9ZCbQJP.mjs} +1 -1
  124. package/dist/{restore-archive-4OP0EYg6.js → restore-archive-4DOUR5Gu.js} +1 -1
  125. package/dist/{restore-archive-YFSJRtKy.mjs → restore-archive-DyOaPeAr.mjs} +1 -1
  126. package/dist/tls/cert-issue.d.ts +16 -1
  127. package/dist/tls/cert-manager.d.ts +6 -0
  128. package/dist/tls/index.d.ts +1 -1
  129. package/dist/{wal-maintenance-TyuOEVy1.mjs → wal-maintenance-BMiX6UIa.mjs} +0 -0
  130. package/dist/{wal-maintenance-Bl0ZKjVR.js → wal-maintenance-DVN-DiVP.js} +0 -0
  131. package/package.json +1 -1
  132. package/dist/auth/admin-token-manager.d.ts +0 -51
  133. package/dist/auth/scoped-token-manager.d.ts +0 -25
@@ -0,0 +1,27 @@
1
+ /**
2
+ * The per-user account preferences kind (D894), owned by `local-auth` because
3
+ * it owns the users the rows belong to — a user's delete wipes them in the
4
+ * same call (`endAllAccess`).
5
+ *
6
+ * Declared on its OWN, never folded into `localAuthRecordKinds`: that map is
7
+ * the phase-6 adoption set, and a kind missing from the catalog there sends
8
+ * the whole auth builtin back to its legacy tables.
9
+ */
10
+ import { type DefinedRecordKind, type PreferenceValue } from "@camstack/types";
11
+ import { z } from "zod";
12
+ /** One `(user, namespace)` row. The record id is {@link userPreferenceRecordId}. */
13
+ export type UserPreferenceBody = {
14
+ readonly userId: string;
15
+ readonly namespace: string;
16
+ readonly value: PreferenceValue;
17
+ readonly revision: number;
18
+ readonly updatedAt: number;
19
+ readonly updatedByDevice: string | null;
20
+ };
21
+ export declare const UserPreferenceBodySchema: z.ZodType<UserPreferenceBody>;
22
+ export type LocalAuthPreferencesRecordKindsMap = {
23
+ readonly "user-preferences": DefinedRecordKind<z.ZodType<UserPreferenceBody>>;
24
+ };
25
+ export declare const localAuthPreferencesRecordKinds: LocalAuthPreferencesRecordKindsMap;
26
+ /** Namespaces cannot hold `:`, so the id splits unambiguously. */
27
+ export declare function userPreferenceRecordId(userId: string, namespace: string): string;
@@ -5,7 +5,10 @@
5
5
  * use `storageAdmin.dropCollection` with `expectRows` after verify (see
6
6
  * `docs/design/records-phase-6-auth-migration.md`).
7
7
  *
8
- * Before hub restart: mint and test an admin token (`csa_`); snapshot every kind.
8
+ * Before hub restart: mint and test an admin token (`auth.mintToken` with `'*'`); snapshot every kind.
9
+ *
10
+ * `scoped-tokens` and `admin-tokens` hold pre-D892 rows read by nothing since
11
+ * D895; they stay declared so the layout is unchanged.
9
12
  */
10
13
  import { ApiKeyRecordSchema, type DefinedRecordKind, type RecordKindHandle, type RecordsAccess, ScopedTokenSchema, TokenScopeSchema, UserRecordSchema } from "@camstack/types";
11
14
  import { z } from "zod";
@@ -128,8 +131,6 @@ export declare const LOCAL_AUTH_RECORDS_LEGACY_COLLECTIONS: readonly string[];
128
131
  export interface LocalAuthRecordStores {
129
132
  readonly users: RecordKindHandle<UserBody>;
130
133
  readonly apiKeys: RecordKindHandle<ApiKeyBody>;
131
- readonly scopedTokens: RecordKindHandle<ScopedTokenBody>;
132
- readonly adminTokens: RecordKindHandle<AdminTokenBody>;
133
134
  readonly loginSessions: RecordKindHandle<LoginSessionBody>;
134
135
  readonly loginSessionCutoffs: RecordKindHandle<LoginSessionCutoffBody>;
135
136
  readonly oauthSessions: RecordKindHandle<OauthSessionBody>;
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Per-user account preferences (D894): one row per `(userId, namespace)`.
3
+ *
4
+ * Every method takes the OWNER's id as its first argument and touches only
5
+ * that user's rows; the hub router stamps it from the principal, and nothing
6
+ * here accepts a second user id.
7
+ *
8
+ * The poll (`changedSince`) is answered from an in-memory mirror of each
9
+ * user's `namespace → revision`, loaded once on first touch. This store is the
10
+ * kind's only writer (one in-process instance in `local-auth`), so the mirror
11
+ * cannot fall behind the rows; a poll that finds nothing changed reads no row.
12
+ * Writes of one user are serialized, so a read-modify-write never interleaves.
13
+ */
14
+ import { type PreferenceRow, type PreferencesChangedSinceOutput, type PreferencesPutInput, type PreferencesPutOutput, type RecordKindHandle } from "@camstack/types";
15
+ import { type UserPreferenceBody } from "./preferences-record-kinds.js";
16
+ export type PreferencesLimitReason = "value-too-large" | "too-many-namespaces";
17
+ /** A write the limits refuse; nothing was stored. */
18
+ export declare class PreferencesLimitError extends Error {
19
+ readonly reason: PreferencesLimitReason;
20
+ constructor(reason: PreferencesLimitReason, message: string);
21
+ }
22
+ /**
23
+ * The limit refusal behind `error`, or `null`. By name and field, not
24
+ * `instanceof`: the store and its caller may load different copies of this
25
+ * module.
26
+ */
27
+ export declare function preferencesLimitReasonOf(error: unknown): PreferencesLimitReason | null;
28
+ export interface UserPreferencesStoreOptions {
29
+ readonly now?: () => number;
30
+ }
31
+ export declare class UserPreferencesStore {
32
+ private readonly kind;
33
+ private readonly now;
34
+ /** `userId → namespace → revision`; absent until the user is first touched. */
35
+ private readonly mirrors;
36
+ private readonly locks;
37
+ constructor(kind: RecordKindHandle<UserPreferenceBody>, options?: UserPreferencesStoreOptions);
38
+ /** The caller's rows; `namespaces` narrows them (unknown ones are simply absent). */
39
+ get(userId: string, namespaces?: readonly string[]): Promise<readonly PreferenceRow[]>;
40
+ put(userId: string, input: PreferencesPutInput): Promise<PreferencesPutOutput>;
41
+ /** `false` when the namespace did not exist. */
42
+ delete(userId: string, namespace: string): Promise<boolean>;
43
+ /**
44
+ * What the client is missing: every row whose revision differs from the one
45
+ * it holds (or that it does not list), and every listed namespace now gone.
46
+ */
47
+ changedSince(userId: string, known: Readonly<Record<string, number>>): Promise<PreferencesChangedSinceOutput>;
48
+ /** Every row of `userId` goes (user delete). Returns how many. */
49
+ wipeUser(userId: string): Promise<number>;
50
+ /** Rows whose user is not in `liveUserIds` go (boot sweep). Returns how many. */
51
+ wipeOrphans(liveUserIds: ReadonlySet<string>): Promise<number>;
52
+ private mirrorOf;
53
+ private scanUser;
54
+ /** Run `task` after every earlier write of the same user has settled. */
55
+ private serialized;
56
+ }
@@ -164,6 +164,14 @@ export declare class LocalNetworkAddon extends BaseAddon<LocalNetworkConfig> {
164
164
  private reconcileMeshEndpoint;
165
165
  private resolvedHttpPort;
166
166
  private tlsDataDir;
167
+ /**
168
+ * The hub's name (D861) for a local root this action may CREATE (an existing root is
169
+ * never renamed). Hub only: on an agent the hub's name would mislabel the agent's own
170
+ * material, so it gets the plain name. A failed read falls back to the machine
171
+ * hostname (what the hub shows when unnamed) and says so: a certificate action must
172
+ * not fail over a label.
173
+ */
174
+ private caHubName;
167
175
  private snapshotTls;
168
176
  /**
169
177
  * Hot-swap the HTTPS listener onto the pair now on disk. A failure is not
@@ -1,7 +1,7 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_chunk = require("../../chunk-Cek0wNdY.js");
3
- const require_dist = require("../../dist-DjQ8C-ku.js");
4
- const require_hub_mdns_ciao = require("../../hub-mdns-ciao-Bkrvgnma.js");
3
+ const require_dist = require("../../dist-C89nqNnn.js");
4
+ const require_hub_mdns_ciao = require("../../hub-mdns-ciao-DB1Y9agt.js");
5
5
  let zod = require("zod");
6
6
  let node_net = require("node:net");
7
7
  let node_os = require("node:os");
@@ -887,7 +887,10 @@ var LocalNetworkAddon = class extends require_dist.BaseAddon {
887
887
  getTlsStatus: async () => this.snapshotTls(),
888
888
  regenerateCertificate: async (_input) => {
889
889
  const extraSans = [...require_hub_mdns_ciao.readExtraSans(this.tlsDataDir())];
890
- await require_hub_mdns_ciao.regenerateCertificate(this.tlsDataDir(), { extraSans });
890
+ await require_hub_mdns_ciao.regenerateCertificate(this.tlsDataDir(), {
891
+ extraSans,
892
+ ...await this.caHubName()
893
+ });
891
894
  this.putCertificateOnListener("regenerate");
892
895
  return this.snapshotTls();
893
896
  },
@@ -910,7 +913,10 @@ var LocalNetworkAddon = class extends require_dist.BaseAddon {
910
913
  },
911
914
  revertToGeneratedCertificate: async () => {
912
915
  const extraSans = [...require_hub_mdns_ciao.readExtraSans(this.tlsDataDir())];
913
- await require_hub_mdns_ciao.revertToGeneratedCertificate(this.tlsDataDir(), { extraSans });
916
+ await require_hub_mdns_ciao.revertToGeneratedCertificate(this.tlsDataDir(), {
917
+ extraSans,
918
+ ...await this.caHubName()
919
+ });
914
920
  this.putCertificateOnListener("revert");
915
921
  return this.snapshotTls();
916
922
  }
@@ -1117,6 +1123,22 @@ var LocalNetworkAddon = class extends require_dist.BaseAddon {
1117
1123
  tlsDataDir() {
1118
1124
  return this.ctx.nodeDataDir ?? this.ctx.dataDir;
1119
1125
  }
1126
+ /**
1127
+ * The hub's name (D861) for a local root this action may CREATE (an existing root is
1128
+ * never renamed). Hub only: on an agent the hub's name would mislabel the agent's own
1129
+ * material, so it gets the plain name. A failed read falls back to the machine
1130
+ * hostname (what the hub shows when unnamed) and says so: a certificate action must
1131
+ * not fail over a label.
1132
+ */
1133
+ async caHubName() {
1134
+ if ((this.ctx.kernel.localNodeId ?? "hub") !== "hub") return {};
1135
+ try {
1136
+ return { hubName: (await this.ctx.api.system.info.query()).serverName.name };
1137
+ } catch (err) {
1138
+ this.ctx.logger.warn("local-network: server name unreadable - naming the CA after the hostname", { meta: { error: require_dist.errMsg(err) } });
1139
+ return { hubName: require_dist.shortHostname(node_os.hostname()) };
1140
+ }
1141
+ }
1120
1142
  snapshotTls() {
1121
1143
  const status = require_hub_mdns_ciao.readTlsAccessStatus(this.tlsDataDir(), require_hub_mdns_ciao.servedHttpsLeafFingerprint());
1122
1144
  return {
@@ -1165,7 +1187,10 @@ var LocalNetworkAddon = class extends require_dist.BaseAddon {
1165
1187
  if (hostname !== "") {
1166
1188
  require_hub_mdns_ciao.writeExtraSans(this.tlsDataDir(), [hostname]);
1167
1189
  if (require_hub_mdns_ciao.readTlsMode(this.tlsDataDir()) === "generated") {
1168
- if ((await require_hub_mdns_ciao.ensureTlsCert(this.tlsDataDir(), { extraSans: [hostname] })).generated) this.putCertificateOnListener("hostname");
1190
+ if ((await require_hub_mdns_ciao.ensureTlsCert(this.tlsDataDir(), {
1191
+ extraSans: [hostname],
1192
+ hubName: require_dist.shortHostname(node_os.hostname())
1193
+ })).generated) this.putCertificateOnListener("hostname");
1169
1194
  }
1170
1195
  } else require_hub_mdns_ciao.writeExtraSans(this.tlsDataDir(), []);
1171
1196
  }
@@ -1,5 +1,5 @@
1
- import { $n as resolveAppConnections, B as HUB_MDNS_TXT_KEYS, Er as validateCustomAddress, On as localNetworkCapability, Wn as pickAppLinkHub, Y as NetworkTunnelStartedDataSchema, hn as isAppLinkBase, kt as checkRoleAddress, l as CAMSTACK_APP_LINK_ORIGIN, or as resolveNotificationTapAddress, qr as BaseAddon, rr as resolveHubAddressRoles, vn as isDefaultAppConnectionEndpoint, ya as EventCategory, z as HUB_MDNS_SERVICE_TYPE } from "../../dist-BpSENkRJ.mjs";
2
- import { P as readLanHttpState, a as storeUploadedCertificate, d as servedHttpsLeafFingerprint, g as readTlsMode, h as readTlsAccessStatus, i as revertToGeneratedCertificate, j as applyLanHttp, k as DEFAULT_LAN_HTTP_PORT, m as readExtraSans, n as applyCertificateToListener, o as useStoredUploadedCertificate, r as regenerateCertificate, t as createCiaoPublisher, v as writeExtraSans, w as ensureTlsCert } from "../../hub-mdns-ciao-DlgkFJVY.mjs";
1
+ import { $n as pickAppLinkHub, Cn as isAppLinkBase, Dn as isDefaultAppConnectionEndpoint, Fr as validateCustomAddress, H as HUB_MDNS_TXT_KEYS, Ln as localNetworkCapability, Lt as checkRoleAddress, Oa as EventCategory, V as HUB_MDNS_SERVICE_TYPE, Z as NetworkTunnelStartedDataSchema, cr as resolveAppConnections, fr as resolveHubAddressRoles, hr as resolveNotificationTapAddress, l as CAMSTACK_APP_LINK_ORIGIN, ni as BaseAddon, ti as errMsg, xr as shortHostname } from "../../dist-Dhux90yB.mjs";
2
+ import { P as readLanHttpState, a as storeUploadedCertificate, d as servedHttpsLeafFingerprint, g as readTlsMode, h as readTlsAccessStatus, i as revertToGeneratedCertificate, j as applyLanHttp, k as DEFAULT_LAN_HTTP_PORT, m as readExtraSans, n as applyCertificateToListener, o as useStoredUploadedCertificate, r as regenerateCertificate, t as createCiaoPublisher, v as writeExtraSans, w as ensureTlsCert } from "../../hub-mdns-ciao-B8olZqqd.mjs";
3
3
  import { z } from "zod";
4
4
  import { isIP } from "node:net";
5
5
  import * as os$1 from "node:os";
@@ -884,7 +884,10 @@ var LocalNetworkAddon = class extends BaseAddon {
884
884
  getTlsStatus: async () => this.snapshotTls(),
885
885
  regenerateCertificate: async (_input) => {
886
886
  const extraSans = [...readExtraSans(this.tlsDataDir())];
887
- await regenerateCertificate(this.tlsDataDir(), { extraSans });
887
+ await regenerateCertificate(this.tlsDataDir(), {
888
+ extraSans,
889
+ ...await this.caHubName()
890
+ });
888
891
  this.putCertificateOnListener("regenerate");
889
892
  return this.snapshotTls();
890
893
  },
@@ -907,7 +910,10 @@ var LocalNetworkAddon = class extends BaseAddon {
907
910
  },
908
911
  revertToGeneratedCertificate: async () => {
909
912
  const extraSans = [...readExtraSans(this.tlsDataDir())];
910
- await revertToGeneratedCertificate(this.tlsDataDir(), { extraSans });
913
+ await revertToGeneratedCertificate(this.tlsDataDir(), {
914
+ extraSans,
915
+ ...await this.caHubName()
916
+ });
911
917
  this.putCertificateOnListener("revert");
912
918
  return this.snapshotTls();
913
919
  }
@@ -1114,6 +1120,22 @@ var LocalNetworkAddon = class extends BaseAddon {
1114
1120
  tlsDataDir() {
1115
1121
  return this.ctx.nodeDataDir ?? this.ctx.dataDir;
1116
1122
  }
1123
+ /**
1124
+ * The hub's name (D861) for a local root this action may CREATE (an existing root is
1125
+ * never renamed). Hub only: on an agent the hub's name would mislabel the agent's own
1126
+ * material, so it gets the plain name. A failed read falls back to the machine
1127
+ * hostname (what the hub shows when unnamed) and says so: a certificate action must
1128
+ * not fail over a label.
1129
+ */
1130
+ async caHubName() {
1131
+ if ((this.ctx.kernel.localNodeId ?? "hub") !== "hub") return {};
1132
+ try {
1133
+ return { hubName: (await this.ctx.api.system.info.query()).serverName.name };
1134
+ } catch (err) {
1135
+ this.ctx.logger.warn("local-network: server name unreadable - naming the CA after the hostname", { meta: { error: errMsg(err) } });
1136
+ return { hubName: shortHostname(os$1.hostname()) };
1137
+ }
1138
+ }
1117
1139
  snapshotTls() {
1118
1140
  const status = readTlsAccessStatus(this.tlsDataDir(), servedHttpsLeafFingerprint());
1119
1141
  return {
@@ -1162,7 +1184,10 @@ var LocalNetworkAddon = class extends BaseAddon {
1162
1184
  if (hostname !== "") {
1163
1185
  writeExtraSans(this.tlsDataDir(), [hostname]);
1164
1186
  if (readTlsMode(this.tlsDataDir()) === "generated") {
1165
- if ((await ensureTlsCert(this.tlsDataDir(), { extraSans: [hostname] })).generated) this.putCertificateOnListener("hostname");
1187
+ if ((await ensureTlsCert(this.tlsDataDir(), {
1188
+ extraSans: [hostname],
1189
+ hubName: shortHostname(os$1.hostname())
1190
+ })).generated) this.putCertificateOnListener("hostname");
1166
1191
  }
1167
1192
  } else writeExtraSans(this.tlsDataDir(), []);
1168
1193
  }
@@ -3,7 +3,9 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-DjQ8C-ku.js");
6
+ const require_dist = require("../../dist-C89nqNnn.js");
7
+ const require_log_read_filter = require("../../log-read-filter-Bi066jwU.js");
8
+ let zod = require("zod");
7
9
  //#region src/builtins/loki-logging/loki-payload.ts
8
10
  /**
9
11
  * Loki rejects a label name that is not a valid Prometheus label
@@ -41,6 +43,7 @@ function toUnixNano(timestamp) {
41
43
  */
42
44
  function formatLokiLine(entry) {
43
45
  const line = { message: entry.message };
46
+ if (entry.id !== void 0) line["id"] = entry.id;
44
47
  if (entry.scope !== void 0 && entry.scope.length > 0) line["scope"] = entry.scope;
45
48
  if (entry.tags !== void 0 && Object.keys(entry.tags).length > 0) line["tags"] = entry.tags;
46
49
  if (entry.meta !== void 0 && Object.keys(entry.meta).length > 0) line["meta"] = entry.meta;
@@ -91,6 +94,231 @@ function buildPushBody(entries, labels) {
91
94
  return { streams };
92
95
  }
93
96
  //#endregion
97
+ //#region src/builtins/loki-logging/loki-read.ts
98
+ /**
99
+ * Reading history back from Loki (D898): `GET /loki/api/v1/query_range`,
100
+ * newest first.
101
+ *
102
+ * The label selector narrows by what IS a label (`job`, `node`, `level` —
103
+ * see `loki-payload.ts` for why nothing else is); line filters (`|=`, `|~`)
104
+ * narrow by device, addon and text on the JSON body; the shared
105
+ * `lineMatches` then decides exactly, so a line filter only has to be a
106
+ * superset.
107
+ *
108
+ * The cursor is the nanosecond timestamp of the oldest line returned plus a
109
+ * short hash of each line returned AT that nanosecond: entries share
110
+ * timestamps (they are millisecond-precise), so "older than t" would drop the
111
+ * siblings a page cut through, and "t or older" would repeat them. The next
112
+ * page asks for `end = t + 1` (Loki's end is exclusive) and skips the hashes.
113
+ */
114
+ var LEVELS = [
115
+ "debug",
116
+ "info",
117
+ "warn",
118
+ "error"
119
+ ];
120
+ /** One query_range window. A busy stream fills `limit` long before this. */
121
+ var LOKI_WINDOW_MS = 360 * 60 * 1e3;
122
+ var MS_NS = 1000000n;
123
+ var CURSOR_PREFIX = "l1.";
124
+ var LokiCursorSchema = zod.z.object({
125
+ /** Nanosecond epoch, decimal string. */
126
+ n: zod.z.string().regex(/^\d{1,22}$/),
127
+ /** Hashes of the lines already returned at exactly `n`. */
128
+ h: zod.z.array(zod.z.string().max(16)).max(1e3)
129
+ });
130
+ function encodeLokiCursor(cursor) {
131
+ return `${CURSOR_PREFIX}${Buffer.from(JSON.stringify(cursor)).toString("base64url")}`;
132
+ }
133
+ function decodeLokiCursor(raw) {
134
+ if (!raw.startsWith(CURSOR_PREFIX)) return null;
135
+ try {
136
+ const parsed = LokiCursorSchema.safeParse(JSON.parse(Buffer.from(raw.slice(3), "base64url").toString("utf8")));
137
+ return parsed.success ? parsed.data : null;
138
+ } catch {
139
+ return null;
140
+ }
141
+ }
142
+ /** FNV-1a, 32 bit, hex — a dedupe key, not a security property. */
143
+ function lineHash(line) {
144
+ let h = 2166136261;
145
+ for (let i = 0; i < line.length; i += 1) {
146
+ h ^= line.charCodeAt(i);
147
+ h = Math.imul(h, 16777619);
148
+ }
149
+ return (h >>> 0).toString(16);
150
+ }
151
+ /** A LogQL double-quoted string literal. */
152
+ function quote(value) {
153
+ return `"${value.replaceAll("\\", "\\\\").replaceAll("\"", "\\\"")}"`;
154
+ }
155
+ function escapeRegex(value) {
156
+ return value.replaceAll(/[\\.+*?()|[\]{}^$]/g, (c) => `\\${c}`);
157
+ }
158
+ /** The LogQL for a read. Pure. */
159
+ function buildLogQl(query, labels) {
160
+ const selectors = [`job=${quote(labels.job)}`];
161
+ if (query.fromNodeId !== void 0) selectors.push(`node=${quote(query.fromNodeId)}`);
162
+ if (query.level !== void 0 && query.level !== "debug") {
163
+ const floor = LEVELS.indexOf(query.level);
164
+ selectors.push(`level=~${quote(LEVELS.slice(floor).join("|"))}`);
165
+ }
166
+ let logql = `{${selectors.join(",")}}`;
167
+ if (query.deviceId !== void 0) logql += ` |= ${quote(`"deviceId":${String(query.deviceId)}`)}`;
168
+ if (query.fromAddonId !== void 0) {
169
+ const bare = query.fromAddonId.split("@")[0] ?? query.fromAddonId;
170
+ logql += ` |= ${quote(`"addonId":"${bare}`)}`;
171
+ }
172
+ const text = query.text?.trim();
173
+ if (text !== void 0 && text.length > 0) logql += ` |~ ${quote(`(?i)${escapeRegex(text)}`)}`;
174
+ return logql;
175
+ }
176
+ var LokiResponseSchema = zod.z.object({ data: zod.z.object({ result: zod.z.array(zod.z.object({
177
+ stream: zod.z.record(zod.z.string(), zod.z.string()),
178
+ values: zod.z.array(zod.z.tuple([zod.z.string(), zod.z.string()]))
179
+ })) }) });
180
+ var LokiLineSchema = zod.z.object({
181
+ id: zod.z.string().optional(),
182
+ message: zod.z.string(),
183
+ scope: zod.z.string().optional(),
184
+ tags: zod.z.record(zod.z.string(), zod.z.unknown()).optional(),
185
+ meta: zod.z.record(zod.z.string(), zod.z.unknown()).optional()
186
+ });
187
+ function levelOf(label) {
188
+ return label === "debug" || label === "warn" || label === "error" ? label : "info";
189
+ }
190
+ /** A query_range body → rows, newest first. Lines that are not ours are dropped. */
191
+ function parseQueryRange(body) {
192
+ const parsed = LokiResponseSchema.safeParse(body);
193
+ if (!parsed.success) throw new Error("Loki answered with an unexpected query_range shape");
194
+ const rows = [];
195
+ for (const stream of parsed.data.data.result) {
196
+ const level = levelOf(stream.stream["level"]);
197
+ for (const [nsText, text] of stream.values) {
198
+ let json;
199
+ try {
200
+ json = JSON.parse(text);
201
+ } catch {
202
+ continue;
203
+ }
204
+ const line = LokiLineSchema.safeParse(json);
205
+ if (!line.success) continue;
206
+ const ns = BigInt(nsText);
207
+ const timestamp = Number(ns / MS_NS);
208
+ const tags = {};
209
+ for (const [k, v] of Object.entries(line.data.tags ?? {})) if (typeof v === "string" || typeof v === "number") tags[k] = v;
210
+ const hasTags = Object.keys(tags).length > 0;
211
+ const entry = {
212
+ ...line.data.id !== void 0 ? { id: line.data.id } : {},
213
+ timestamp,
214
+ level,
215
+ ...line.data.scope !== void 0 ? { scope: line.data.scope } : {},
216
+ message: line.data.message,
217
+ ...hasTags ? { tags } : {},
218
+ ...line.data.meta !== void 0 ? { meta: line.data.meta } : {}
219
+ };
220
+ rows.push({
221
+ ns,
222
+ hash: lineHash(text),
223
+ entry,
224
+ line: {
225
+ timestamp,
226
+ level,
227
+ message: line.data.message,
228
+ scope: line.data.scope,
229
+ tags: hasTags ? tags : void 0,
230
+ meta: line.data.meta
231
+ }
232
+ });
233
+ }
234
+ }
235
+ return rows.toSorted((a, b) => a.ns < b.ns ? 1 : a.ns > b.ns ? -1 : 0);
236
+ }
237
+ /**
238
+ * One backward slice over Loki: windows of {@link LOKI_WINDOW_MS}, newest
239
+ * first, until `limit` matches, the horizon, the budget or a cancel.
240
+ */
241
+ async function readLokiBackward(args) {
242
+ const { query } = args;
243
+ const now = args.now ?? Date.now;
244
+ const filter = require_log_read_filter.compileLogFilter(query);
245
+ const limit = query.limit ?? 200;
246
+ const deadline = now() + Math.min(query.budgetMs ?? 2e3, 1e4);
247
+ const logql = buildLogQl(query, args.labels);
248
+ const horizonMs = query.since ?? now() - 30 * 864e5;
249
+ const horizonNs = BigInt(Math.trunc(horizonMs)) * MS_NS;
250
+ const cursor = query.before !== void 0 ? decodeLokiCursor(query.before) : null;
251
+ const untilNs = query.until !== void 0 ? BigInt(Math.trunc(query.until) + 1) * MS_NS : null;
252
+ let endNs = cursor !== null ? BigInt(cursor.n) + 1n : untilNs ?? BigInt(now() + 1) * MS_NS;
253
+ let seenAtBoundary = new Set(cursor?.h ?? []);
254
+ let boundaryNs = cursor !== null ? BigInt(cursor.n) : null;
255
+ const entries = [];
256
+ let bytes = 0;
257
+ while (endNs > horizonNs) {
258
+ if (args.isCancelled()) return {
259
+ entries,
260
+ nextBefore: encodeLokiCursor({
261
+ n: String(endNs - 1n),
262
+ h: [...seenAtBoundary]
263
+ }),
264
+ nextAfter: null,
265
+ scannedBytes: bytes,
266
+ done: false,
267
+ cancelled: true
268
+ };
269
+ const windowStart = endNs - BigInt(LOKI_WINDOW_MS) * MS_NS;
270
+ const startNs = windowStart > horizonNs ? windowStart : horizonNs;
271
+ const want = limit - entries.length + seenAtBoundary.size;
272
+ const { rows, bytes: got } = await args.range({
273
+ logql,
274
+ startNs,
275
+ endNs,
276
+ limit: want
277
+ });
278
+ bytes += got;
279
+ let oldest = null;
280
+ for (const row of rows) {
281
+ if (boundaryNs !== null && row.ns === boundaryNs && seenAtBoundary.has(row.hash)) continue;
282
+ if (oldest === null || row.ns < oldest) {
283
+ if (oldest !== null && row.ns < oldest) seenAtBoundary = /* @__PURE__ */ new Set();
284
+ oldest = row.ns;
285
+ }
286
+ if (row.ns === oldest) seenAtBoundary.add(row.hash);
287
+ boundaryNs = oldest;
288
+ if (!require_log_read_filter.lineMatches(row.line, filter)) continue;
289
+ entries.push(row.entry);
290
+ if (entries.length >= limit) return {
291
+ entries,
292
+ nextBefore: encodeLokiCursor({
293
+ n: String(row.ns),
294
+ h: [...seenAtBoundary]
295
+ }),
296
+ nextAfter: null,
297
+ scannedBytes: bytes,
298
+ done: false
299
+ };
300
+ }
301
+ if (rows.length >= want && oldest !== null) endNs = oldest + 1n;
302
+ else {
303
+ endNs = startNs;
304
+ seenAtBoundary = /* @__PURE__ */ new Set();
305
+ boundaryNs = null;
306
+ }
307
+ if (now() >= deadline) break;
308
+ }
309
+ const done = endNs <= horizonNs;
310
+ return {
311
+ entries,
312
+ nextBefore: done ? null : encodeLokiCursor({
313
+ n: String(endNs - 1n),
314
+ h: [...seenAtBoundary]
315
+ }),
316
+ nextAfter: null,
317
+ scannedBytes: bytes,
318
+ done
319
+ };
320
+ }
321
+ //#endregion
94
322
  //#region src/builtins/loki-logging/loki-destination.ts
95
323
  var LEVEL_ORDER = {
96
324
  debug: 0,
@@ -114,6 +342,8 @@ var LokiDestination = class {
114
342
  failures = 0;
115
343
  lastError = null;
116
344
  lastPushAtMs = null;
345
+ /** In-flight history reads, by requestId, so `cancelRead` can abort the fetch. */
346
+ reads = /* @__PURE__ */ new Map();
117
347
  fetchFn;
118
348
  setIntervalFn;
119
349
  clearIntervalFn;
@@ -121,7 +351,7 @@ var LokiDestination = class {
121
351
  this.fetchFn = deps?.fetchFn ?? ((url, init) => globalThis.fetch(url, {
122
352
  method: init.method,
123
353
  headers: { ...init.headers },
124
- body: init.body,
354
+ ...init.body !== void 0 ? { body: init.body } : {},
125
355
  ...init.signal ? { signal: init.signal } : {}
126
356
  }));
127
357
  this.setIntervalFn = deps?.setIntervalFn ?? setInterval;
@@ -213,9 +443,10 @@ var LokiDestination = class {
213
443
  }
214
444
  async push(body, config) {
215
445
  const endpoint = `${config.url.replace(/\/+$/, "")}/loki/api/v1/push`;
216
- const headers = { "content-type": "application/json" };
217
- if (config.tenantId.trim().length > 0) headers["X-Scope-OrgID"] = config.tenantId.trim();
218
- if (config.username.length > 0) headers["authorization"] = `Basic ${Buffer.from(`${config.username}:${config.password}`).toString("base64")}`;
446
+ const headers = {
447
+ "content-type": "application/json",
448
+ ...this.authHeaders(config)
449
+ };
219
450
  const controller = new AbortController();
220
451
  const timeout = setTimeout(() => controller.abort(), Math.max(1e3, config.timeoutMs));
221
452
  try {
@@ -283,8 +514,93 @@ var LokiDestination = class {
283
514
  };
284
515
  }
285
516
  }
286
- async query(_filter) {
287
- return [];
517
+ /** Headers every call to this Loki carries: tenant + basic auth. */
518
+ authHeaders(config) {
519
+ const headers = {};
520
+ if (config.tenantId.trim().length > 0) headers["X-Scope-OrgID"] = config.tenantId.trim();
521
+ if (config.username.length > 0) headers["authorization"] = `Basic ${Buffer.from(`${config.username}:${config.password}`).toString("base64")}`;
522
+ return headers;
523
+ }
524
+ /**
525
+ * Readable when it is configured (D898): the same URL and credentials the
526
+ * push uses answer `query_range`. Labels are `job`/`node`/`level`, so those
527
+ * filter at the index; device, addon and text are line filters.
528
+ */
529
+ async describeRead() {
530
+ const configured = this.isActive();
531
+ return {
532
+ readable: configured,
533
+ reason: configured ? null : "Loki is not enabled or has no URL",
534
+ filters: {
535
+ deviceId: true,
536
+ addonId: true,
537
+ nodeId: true,
538
+ tags: true,
539
+ level: true,
540
+ text: true,
541
+ timeRange: true
542
+ },
543
+ retentionDays: null
544
+ };
545
+ }
546
+ /** One backward slice from `query_range`, newest first. Never logs. */
547
+ async read(query) {
548
+ const config = this.config;
549
+ if (config === null || !this.isActive()) throw new Error("Loki is not enabled or has no URL");
550
+ const controller = new AbortController();
551
+ const requestId = query.requestId;
552
+ if (requestId !== void 0) this.reads.set(requestId, controller);
553
+ const base = config.url.replace(/\/+$/, "");
554
+ const range = async ({ logql, startNs, endNs, limit }) => {
555
+ const params = new URLSearchParams({
556
+ query: logql,
557
+ start: String(startNs),
558
+ end: String(endNs),
559
+ limit: String(limit),
560
+ direction: "backward"
561
+ });
562
+ const timeout = setTimeout(() => controller.abort(), Math.max(1e3, config.timeoutMs));
563
+ try {
564
+ const response = await this.fetchFn(`${base}/loki/api/v1/query_range?${params.toString()}`, {
565
+ method: "GET",
566
+ headers: this.authHeaders(config),
567
+ signal: controller.signal
568
+ });
569
+ const text = await response.text();
570
+ if (!response.ok) throw new Error(`Loki query failed: ${String(response.status)}${text.length > 0 ? ` — ${text.slice(0, 200)}` : ""}`);
571
+ return {
572
+ rows: parseQueryRange(JSON.parse(text)),
573
+ bytes: Buffer.byteLength(text)
574
+ };
575
+ } finally {
576
+ clearTimeout(timeout);
577
+ }
578
+ };
579
+ try {
580
+ return await readLokiBackward({
581
+ query,
582
+ labels: { job: config.job },
583
+ range,
584
+ isCancelled: () => controller.signal.aborted
585
+ });
586
+ } catch (error) {
587
+ if (controller.signal.aborted) return {
588
+ entries: [],
589
+ nextBefore: query.before ?? null,
590
+ nextAfter: null,
591
+ scannedBytes: 0,
592
+ done: false,
593
+ cancelled: true
594
+ };
595
+ throw error;
596
+ } finally {
597
+ if (requestId !== void 0) this.reads.delete(requestId);
598
+ }
599
+ }
600
+ async cancelRead(input) {
601
+ const controller = this.reads.get(input.requestId);
602
+ controller?.abort();
603
+ return { cancelled: controller !== void 0 };
288
604
  }
289
605
  async shutdown() {
290
606
  this.stopTimer();