@quolu/lattice 0.52.2 → 0.52.4
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/package.json +1 -1
- package/src/artifact-contracts-v2.mjs +325 -325
- package/src/artifact-contracts.mjs +895 -895
- package/src/boundary-compiler.mjs +712 -712
- package/src/boundary-observation-compiler-v2.mjs +344 -344
- package/src/bounded-seam.mjs +230 -230
- package/src/bridge-address.mjs +107 -107
- package/src/bridge-cli.mjs +297 -297
- package/src/bridge-config.mjs +346 -346
- package/src/bridge-daemon.mjs +378 -378
- package/src/bridge-hub-protocol.mjs +198 -0
- package/src/bridge-launch-agent.mjs +323 -323
- package/src/bridge-registrar.mjs +102 -102
- package/src/bridge-server.mjs +376 -376
- package/src/cli-help.mjs +308 -308
- package/src/cli-stdio.mjs +40 -40
- package/src/control-compiler.mjs +532 -532
- package/src/dag-chain.mjs +261 -261
- package/src/factory-diagnostics.mjs +188 -188
- package/src/git-process.mjs +72 -0
- package/src/hash-chain.mjs +76 -76
- package/src/hooks-cli.mjs +1057 -1057
- package/src/isolation-runner.mjs +409 -409
- package/src/node-version-guard.mjs +44 -44
- package/src/project-cli.mjs +699 -697
- package/src/project-identity.mjs +130 -130
- package/src/rc1-black-box-oracle.mjs +906 -906
- package/src/rc1-comparison.mjs +154 -154
- package/src/rc1-evidence-bundle.mjs +456 -456
- package/src/rc1-v4-campaign.mjs +708 -708
- package/src/rc1-v4-transform.mjs +467 -467
- package/src/rc1-v5-artifact-set.mjs +855 -855
- package/src/rc1-v5-behavior-evidence.mjs +594 -594
- package/src/rc1-v5-campaign.mjs +797 -797
- package/src/rc1-v5-transform.mjs +421 -421
- package/src/rc1-v6-artifact-set.mjs +807 -807
- package/src/rc1-v6-behavior-evidence.mjs +273 -273
- package/src/rc1-v6-campaign.mjs +623 -623
- package/src/rc1-v6-causal-binding.mjs +473 -473
- package/src/rc1-v6-measurement.mjs +313 -313
- package/src/rc2-artifact-set.mjs +1584 -1584
- package/src/rc2-campaign.mjs +1506 -1506
- package/src/rc2-delivery-policy-front-end.mjs +1079 -1079
- package/src/rc2-delivery-policy-oracle.mjs +134 -134
- package/src/rc2-delivery-policy-transform.mjs +1127 -1127
- package/src/rc2-rc1-transfer-front-end.mjs +511 -511
- package/src/rc3-actual-dogfood.mjs +652 -652
- package/src/rc3-dogfood-scaffold.mjs +315 -315
- package/src/rc3-scripted-campaign.mjs +1383 -1383
- package/src/rc4-stage1-dogfood.mjs +673 -673
- package/src/runtime-adapter-registry.mjs +524 -524
- package/src/runtime-cli.mjs +4588 -4588
- package/src/runtime-contracts.mjs +824 -824
- package/src/runtime-control-store.mjs +604 -604
- package/src/runtime-controller-protocol.mjs +587 -587
- package/src/runtime-decision-verifier.mjs +701 -701
- package/src/runtime-diff-observer.mjs +361 -361
- package/src/runtime-direct-os-observer.mjs +301 -301
- package/src/runtime-driver-state.mjs +166 -166
- package/src/runtime-engine.mjs +779 -779
- package/src/runtime-errors.mjs +356 -356
- package/src/runtime-event-store.mjs +189 -189
- package/src/runtime-front-end.mjs +925 -925
- package/src/runtime-gate-store.mjs +481 -481
- package/src/runtime-hold-recompile.mjs +916 -916
- package/src/runtime-io-sentinel.mjs +391 -391
- package/src/runtime-lifecycle-lock.mjs +294 -294
- package/src/runtime-managed-supervisor.mjs +1490 -1490
- package/src/runtime-multi-epoch-store.mjs +838 -838
- package/src/runtime-projection.mjs +269 -269
- package/src/runtime-pull-intake.mjs +1192 -1192
- package/src/runtime-scripted-adapter-controller.mjs +1160 -1160
- package/src/runtime-scripted-executor.mjs +163 -163
- package/src/runtime-scripted-worktree.mjs +104 -104
- package/src/runtime-seam-resolve.mjs +428 -428
- package/src/runtime-seam-treatment.mjs +173 -173
- package/src/runtime-socket-owner.mjs +125 -125
- package/src/runtime-work-order-contracts.mjs +91 -91
- package/src/runtime-work-order-controller.mjs +1171 -1171
- package/src/runtime-worktree-executor.mjs +199 -199
- package/src/schedulability-compiler-v2.mjs +303 -303
- package/src/schedulability-verifier-v2.mjs +317 -317
- package/src/seam-apply.mjs +549 -549
- package/src/seam-commit-shared.mjs +22 -22
- package/src/seam-commit-transform.mjs +81 -81
- package/src/seam-commit.mjs +18 -18
- package/src/seam-cost.mjs +322 -322
- package/src/seam-derivation.mjs +188 -188
- package/src/seam-gate.mjs +146 -146
- package/src/seam-proposal-contracts.mjs +446 -446
- package/src/seam-proposal-queries.mjs +521 -521
- package/src/seam-proposal.mjs +2011 -2011
- package/src/seam-ref.mjs +33 -33
- package/src/seam-rewrite.mjs +286 -286
- package/src/seam-transform.mjs +554 -554
- package/src/seam-verification.mjs +260 -260
- package/src/sensor-adapter.mjs +432 -432
- package/src/sensor-cli.mjs +139 -139
- package/src/sensor-diff.mjs +661 -661
- package/src/sensor-node-runtime.mjs +53 -53
- package/src/sensor-runtime.mjs +52 -52
- package/src/timestamp-contract.mjs +8 -8
- package/src/todo-audit-pending.mjs +91 -91
- package/src/todo-chain.mjs +178 -178
- package/src/todo-cli.mjs +3143 -3141
- package/src/todo-contracts.mjs +728 -728
- package/src/todo-dashboard-registry.mjs +573 -573
- package/src/todo-dispatch-shape.mjs +190 -190
- package/src/todo-gantt-html-independence.mjs +239 -239
- package/src/todo-gantt-html-shared.mjs +226 -226
- package/src/todo-gantt-html-style.mjs +131 -131
- package/src/todo-gantt-html.mjs +248 -248
- package/src/todo-gantt-layout.mjs +974 -974
- package/src/todo-gantt-live.mjs +361 -361
- package/src/todo-gantt-nested.mjs +263 -263
- package/src/todo-gantt-presentation.mjs +217 -217
- package/src/todo-gantt-scope.mjs +123 -123
- package/src/todo-gantt-svg.mjs +353 -353
- package/src/todo-independence-contracts.mjs +595 -595
- package/src/todo-independence-guidance.mjs +322 -322
- package/src/todo-independence.mjs +640 -640
- package/src/todo-markdown-renderer.mjs +260 -260
- package/src/todo-migration.mjs +448 -448
- package/src/todo-narrative-anchor.mjs +130 -130
- package/src/todo-note-store.mjs +629 -629
- package/src/todo-parallel-candidates.mjs +114 -114
- package/src/todo-revision.mjs +995 -995
- package/src/todo-split.mjs +472 -472
- package/src/todo-status.mjs +690 -690
- package/src/todo-store-git-transaction.mjs +418 -418
- package/src/todo-store.mjs +4360 -4322
- package/src/treatment-compiler.mjs +728 -728
- package/src/treatment-runner.mjs +656 -656
- package/src/witness-scaffold.mjs +180 -180
package/src/bridge-address.mjs
CHANGED
|
@@ -1,107 +1,107 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Bridge listen-address resolution.
|
|
3
|
-
*
|
|
4
|
-
* The bridge is configured with a literal IP so the operator controls exactly
|
|
5
|
-
* which interface it is exposed on. A DHCP lease change silently invalidates
|
|
6
|
-
* that choice: the address disappears from the host, the old socket lingers on
|
|
7
|
-
* a dead address, and nothing on the LAN can reach the bridge any more —
|
|
8
|
-
* while `bridge status` keeps reporting `enabled: true`.
|
|
9
|
-
*
|
|
10
|
-
* This module answers two questions without any I/O:
|
|
11
|
-
* 1. is the configured address still present on this host?
|
|
12
|
-
* 2. if not, is there an address that is obviously the same intent?
|
|
13
|
-
*
|
|
14
|
-
* "Same intent" is deliberately narrow. A DHCP lease change moves the host
|
|
15
|
-
* inside its own subnet, so only a non-internal address of the same family in
|
|
16
|
-
* the same subnet (IPv4 /24, IPv6 /64) is adopted. An address on a different
|
|
17
|
-
* network — a VPN, a second NIC, a public interface — is never adopted
|
|
18
|
-
* automatically, because that would expose the bridge somewhere the operator
|
|
19
|
-
* did not choose. Loopback is never adopted either: it would silently take the
|
|
20
|
-
* bridge off the LAN while still looking healthy.
|
|
21
|
-
*/
|
|
22
|
-
|
|
23
|
-
import { isIP } from 'node:net';
|
|
24
|
-
|
|
25
|
-
/** Wildcards already bind every interface, so they are never "absent". */
|
|
26
|
-
const WILDCARDS = new Set(['0.0.0.0', '::']);
|
|
27
|
-
|
|
28
|
-
export const BRIDGE_LISTEN_STATES = Object.freeze([
|
|
29
|
-
'present', 'rebindable', 'absent', 'unconfigured',
|
|
30
|
-
]);
|
|
31
|
-
|
|
32
|
-
function normalize(value) {
|
|
33
|
-
return typeof value === 'string' ? value.trim().toLowerCase() : '';
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Flatten `os.networkInterfaces()` into the addresses a bridge may bind.
|
|
38
|
-
* Internal (loopback) addresses are kept but marked, so callers can exclude
|
|
39
|
-
* them from adoption while still recognising an explicitly configured one.
|
|
40
|
-
*/
|
|
41
|
-
export function bridgeHostAddresses(interfaces = {}) {
|
|
42
|
-
const result = [];
|
|
43
|
-
for (const entries of Object.values(interfaces ?? {})) {
|
|
44
|
-
for (const entry of entries ?? []) {
|
|
45
|
-
const address = normalize(entry?.address);
|
|
46
|
-
if (address === '' || isIP(address) === 0) continue;
|
|
47
|
-
result.push({ address, family: isIP(address), internal: entry?.internal === true });
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
return result.sort((left, right) => (left.address < right.address ? -1
|
|
51
|
-
: left.address > right.address ? 1 : 0));
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/** IPv4 /24 or IPv6 /64 prefix, used as the "same network" test. */
|
|
55
|
-
function subnetKey(address) {
|
|
56
|
-
const family = isIP(address);
|
|
57
|
-
if (family === 4) return address.split('.').slice(0, 3).join('.');
|
|
58
|
-
if (family !== 6) return null;
|
|
59
|
-
// Expand the IPv6 form enough to compare the first four hextets.
|
|
60
|
-
const [head] = address.split('%');
|
|
61
|
-
const parts = head.split('::');
|
|
62
|
-
const left = parts[0] === '' ? [] : parts[0].split(':');
|
|
63
|
-
const right = parts.length > 1 ? (parts[1] === '' ? [] : parts[1].split(':')) : [];
|
|
64
|
-
const fill = Array.from({ length: Math.max(0, 8 - left.length - right.length) }, () => '0');
|
|
65
|
-
const hextets = (parts.length > 1 ? [...left, ...fill, ...right] : left)
|
|
66
|
-
.map((entry) => entry.replace(/^0+(?=[0-9a-f])/u, ''));
|
|
67
|
-
return hextets.slice(0, 4).join(':');
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Decide which address the bridge should actually listen on.
|
|
72
|
-
*
|
|
73
|
-
* @param {object} options
|
|
74
|
-
* @param {string|null} options.configured configured literal address.
|
|
75
|
-
* @param {object} [options.interfaces] `os.networkInterfaces()` shaped object.
|
|
76
|
-
* @returns {{state: string, effective: string|null, configured: string|null,
|
|
77
|
-
* candidates: string[], reason: string|null}}
|
|
78
|
-
*/
|
|
79
|
-
export function resolveBridgeListenAddress({ configured, interfaces = {} } = {}) {
|
|
80
|
-
const wanted = normalize(configured);
|
|
81
|
-
if (wanted === '' || isIP(wanted) === 0) {
|
|
82
|
-
return { state: 'unconfigured', effective: null, configured: null, candidates: [], reason: 'listen_address_unconfigured' };
|
|
83
|
-
}
|
|
84
|
-
if (WILDCARDS.has(wanted)) {
|
|
85
|
-
return { state: 'present', effective: wanted, configured: wanted, candidates: [], reason: null };
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
const addresses = bridgeHostAddresses(interfaces);
|
|
89
|
-
if (addresses.some((entry) => entry.address === wanted)) {
|
|
90
|
-
return { state: 'present', effective: wanted, configured: wanted, candidates: [], reason: null };
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
const family = isIP(wanted);
|
|
94
|
-
const wantedSubnet = subnetKey(wanted);
|
|
95
|
-
const candidates = addresses
|
|
96
|
-
.filter((entry) => !entry.internal && entry.family === family
|
|
97
|
-
&& wantedSubnet !== null && subnetKey(entry.address) === wantedSubnet)
|
|
98
|
-
.map((entry) => entry.address);
|
|
99
|
-
|
|
100
|
-
if (candidates.length === 0) {
|
|
101
|
-
return { state: 'absent', effective: null, configured: wanted, candidates: [],
|
|
102
|
-
reason: 'configured_address_absent_from_host' };
|
|
103
|
-
}
|
|
104
|
-
// Deterministic pick; `candidates` is reported so an ambiguous host is visible.
|
|
105
|
-
return { state: 'rebindable', effective: candidates[0], configured: wanted, candidates,
|
|
106
|
-
reason: 'configured_address_absent_rebound_within_subnet' };
|
|
107
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Bridge listen-address resolution.
|
|
3
|
+
*
|
|
4
|
+
* The bridge is configured with a literal IP so the operator controls exactly
|
|
5
|
+
* which interface it is exposed on. A DHCP lease change silently invalidates
|
|
6
|
+
* that choice: the address disappears from the host, the old socket lingers on
|
|
7
|
+
* a dead address, and nothing on the LAN can reach the bridge any more —
|
|
8
|
+
* while `bridge status` keeps reporting `enabled: true`.
|
|
9
|
+
*
|
|
10
|
+
* This module answers two questions without any I/O:
|
|
11
|
+
* 1. is the configured address still present on this host?
|
|
12
|
+
* 2. if not, is there an address that is obviously the same intent?
|
|
13
|
+
*
|
|
14
|
+
* "Same intent" is deliberately narrow. A DHCP lease change moves the host
|
|
15
|
+
* inside its own subnet, so only a non-internal address of the same family in
|
|
16
|
+
* the same subnet (IPv4 /24, IPv6 /64) is adopted. An address on a different
|
|
17
|
+
* network — a VPN, a second NIC, a public interface — is never adopted
|
|
18
|
+
* automatically, because that would expose the bridge somewhere the operator
|
|
19
|
+
* did not choose. Loopback is never adopted either: it would silently take the
|
|
20
|
+
* bridge off the LAN while still looking healthy.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import { isIP } from 'node:net';
|
|
24
|
+
|
|
25
|
+
/** Wildcards already bind every interface, so they are never "absent". */
|
|
26
|
+
const WILDCARDS = new Set(['0.0.0.0', '::']);
|
|
27
|
+
|
|
28
|
+
export const BRIDGE_LISTEN_STATES = Object.freeze([
|
|
29
|
+
'present', 'rebindable', 'absent', 'unconfigured',
|
|
30
|
+
]);
|
|
31
|
+
|
|
32
|
+
function normalize(value) {
|
|
33
|
+
return typeof value === 'string' ? value.trim().toLowerCase() : '';
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Flatten `os.networkInterfaces()` into the addresses a bridge may bind.
|
|
38
|
+
* Internal (loopback) addresses are kept but marked, so callers can exclude
|
|
39
|
+
* them from adoption while still recognising an explicitly configured one.
|
|
40
|
+
*/
|
|
41
|
+
export function bridgeHostAddresses(interfaces = {}) {
|
|
42
|
+
const result = [];
|
|
43
|
+
for (const entries of Object.values(interfaces ?? {})) {
|
|
44
|
+
for (const entry of entries ?? []) {
|
|
45
|
+
const address = normalize(entry?.address);
|
|
46
|
+
if (address === '' || isIP(address) === 0) continue;
|
|
47
|
+
result.push({ address, family: isIP(address), internal: entry?.internal === true });
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return result.sort((left, right) => (left.address < right.address ? -1
|
|
51
|
+
: left.address > right.address ? 1 : 0));
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** IPv4 /24 or IPv6 /64 prefix, used as the "same network" test. */
|
|
55
|
+
function subnetKey(address) {
|
|
56
|
+
const family = isIP(address);
|
|
57
|
+
if (family === 4) return address.split('.').slice(0, 3).join('.');
|
|
58
|
+
if (family !== 6) return null;
|
|
59
|
+
// Expand the IPv6 form enough to compare the first four hextets.
|
|
60
|
+
const [head] = address.split('%');
|
|
61
|
+
const parts = head.split('::');
|
|
62
|
+
const left = parts[0] === '' ? [] : parts[0].split(':');
|
|
63
|
+
const right = parts.length > 1 ? (parts[1] === '' ? [] : parts[1].split(':')) : [];
|
|
64
|
+
const fill = Array.from({ length: Math.max(0, 8 - left.length - right.length) }, () => '0');
|
|
65
|
+
const hextets = (parts.length > 1 ? [...left, ...fill, ...right] : left)
|
|
66
|
+
.map((entry) => entry.replace(/^0+(?=[0-9a-f])/u, ''));
|
|
67
|
+
return hextets.slice(0, 4).join(':');
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Decide which address the bridge should actually listen on.
|
|
72
|
+
*
|
|
73
|
+
* @param {object} options
|
|
74
|
+
* @param {string|null} options.configured configured literal address.
|
|
75
|
+
* @param {object} [options.interfaces] `os.networkInterfaces()` shaped object.
|
|
76
|
+
* @returns {{state: string, effective: string|null, configured: string|null,
|
|
77
|
+
* candidates: string[], reason: string|null}}
|
|
78
|
+
*/
|
|
79
|
+
export function resolveBridgeListenAddress({ configured, interfaces = {} } = {}) {
|
|
80
|
+
const wanted = normalize(configured);
|
|
81
|
+
if (wanted === '' || isIP(wanted) === 0) {
|
|
82
|
+
return { state: 'unconfigured', effective: null, configured: null, candidates: [], reason: 'listen_address_unconfigured' };
|
|
83
|
+
}
|
|
84
|
+
if (WILDCARDS.has(wanted)) {
|
|
85
|
+
return { state: 'present', effective: wanted, configured: wanted, candidates: [], reason: null };
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const addresses = bridgeHostAddresses(interfaces);
|
|
89
|
+
if (addresses.some((entry) => entry.address === wanted)) {
|
|
90
|
+
return { state: 'present', effective: wanted, configured: wanted, candidates: [], reason: null };
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const family = isIP(wanted);
|
|
94
|
+
const wantedSubnet = subnetKey(wanted);
|
|
95
|
+
const candidates = addresses
|
|
96
|
+
.filter((entry) => !entry.internal && entry.family === family
|
|
97
|
+
&& wantedSubnet !== null && subnetKey(entry.address) === wantedSubnet)
|
|
98
|
+
.map((entry) => entry.address);
|
|
99
|
+
|
|
100
|
+
if (candidates.length === 0) {
|
|
101
|
+
return { state: 'absent', effective: null, configured: wanted, candidates: [],
|
|
102
|
+
reason: 'configured_address_absent_from_host' };
|
|
103
|
+
}
|
|
104
|
+
// Deterministic pick; `candidates` is reported so an ambiguous host is visible.
|
|
105
|
+
return { state: 'rebindable', effective: candidates[0], configured: wanted, candidates,
|
|
106
|
+
reason: 'configured_address_absent_rebound_within_subnet' };
|
|
107
|
+
}
|