@githolon/dsl 0.111.0 → 0.112.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dart/CHANGELOG.md +11 -3
- package/dart/lib/src/reception/dart_reception.dart +48 -30
- package/dart/lib/src/runner/node-entry.mjs +134 -19
- package/dart/lib/src/runtime_v2_gateway.dart +1 -22
- package/dart/lib/src/runtime_v2_transport.dart +5 -1
- package/dart/lib/src/write_progress.dart +48 -0
- package/dart/pubspec.lock +1 -1
- package/dart/pubspec.yaml +2 -2
- package/dart/test/child_authentication_repair_test.dart +46 -0
- package/dart/test/runtime_v2_gateway_test.dart +29 -0
- package/dart/test/write_progress_test.dart +56 -0
- package/dart_types/CHANGELOG.md +4 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/observability_desk.pb.dart +90 -1
- package/dart_types/lib/src/runtime_outcome.dart +46 -31
- package/dart_types/pubspec.yaml +1 -1
- package/inspect_release.mjs +42 -0
- package/package.json +4 -3
- package/src/client_inventory_metadata.ts +9 -0
- package/src/codegen_dart_facade.ts +1 -0
- package/src/compile_package_main.ts +9 -3
- package/src/inspect_release_main.ts +17 -0
- package/src/law_units.ts +1 -1
- package/src/release_inspection.ts +147 -0
package/dart/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
## 0.66.9
|
|
2
|
+
|
|
3
|
+
- Prepare delayed workspace enrollment inside Peer Office and retry a proven non-admitted write once. Generated applications can observe preparation while retaining their draft.
|
|
4
|
+
|
|
5
|
+
## 0.66.8
|
|
6
|
+
|
|
7
|
+
- Recover expired or missing sync credentials from typed failures. Preserve authorization and domain refusals without refreshing credentials based on diagnostic wording or HTTP 403.
|
|
8
|
+
|
|
1
9
|
## 0.66.7
|
|
2
10
|
|
|
3
11
|
- Recover sync after loss of the upstream bookmark by fetching provider history and allowing the kernel to verify a shared ancestor. Preserve pending local work and reject unrelated history through the existing admission gate.
|
|
@@ -18,7 +26,7 @@
|
|
|
18
26
|
|
|
19
27
|
## 0.66.3
|
|
20
28
|
|
|
21
|
-
-
|
|
29
|
+
- Recover expired or missing sync credentials from typed failures. Preserve authorization and domain refusals without refreshing credentials based on diagnostic wording or HTTP 403.
|
|
22
30
|
|
|
23
31
|
## 0.66.2
|
|
24
32
|
|
|
@@ -114,11 +122,11 @@
|
|
|
114
122
|
|
|
115
123
|
## 0.64.47
|
|
116
124
|
|
|
117
|
-
-
|
|
125
|
+
- Recover expired or missing sync credentials from typed failures. Preserve authorization and domain refusals without refreshing credentials based on diagnostic wording or HTTP 403.
|
|
118
126
|
|
|
119
127
|
## 0.64.46
|
|
120
128
|
|
|
121
|
-
-
|
|
129
|
+
- Recover expired or missing sync credentials from typed failures. Preserve authorization and domain refusals without refreshing credentials based on diagnostic wording or HTTP 403.
|
|
122
130
|
|
|
123
131
|
## 0.64.45
|
|
124
132
|
|
|
@@ -17,6 +17,7 @@ library;
|
|
|
17
17
|
import 'package:meta/meta.dart' show internal;
|
|
18
18
|
|
|
19
19
|
import 'dart:async';
|
|
20
|
+
import '../write_progress.dart';
|
|
20
21
|
import 'dart:convert';
|
|
21
22
|
import 'dart:developer' as developer;
|
|
22
23
|
import 'dart:typed_data';
|
|
@@ -879,7 +880,16 @@ class DartReception {
|
|
|
879
880
|
);
|
|
880
881
|
}
|
|
881
882
|
|
|
883
|
+
final Map<String, void Function(NomosWriteProgress)> _writeObservers = {};
|
|
884
|
+
|
|
882
885
|
void _onRuntimeEvent(nomos_protocol.RuntimeEvent event) {
|
|
886
|
+
if (event.hasWritePreparation()) {
|
|
887
|
+
final progress = event.writePreparation;
|
|
888
|
+
_eventPlane._writeObservers[progress.requestId]?.call(
|
|
889
|
+
const NomosWriteProgress(NomosWritePhase.preparing),
|
|
890
|
+
);
|
|
891
|
+
return;
|
|
892
|
+
}
|
|
883
893
|
if (event.hasReady()) {
|
|
884
894
|
final stale = _staleBundleError(event.ready.runtimeVersion);
|
|
885
895
|
if (stale != null && !allowStaleRunnerBundle) {
|
|
@@ -1719,40 +1729,48 @@ class DartReception {
|
|
|
1719
1729
|
// ignore: avoid_print
|
|
1720
1730
|
print('[nomos-offer] dispatch ${call.domain}/${call.directive} in '
|
|
1721
1731
|
'$workspace using ${authenticationProof.name}');
|
|
1722
|
-
Future<nomos_protocol.CallResponse> attempt() {
|
|
1732
|
+
Future<nomos_protocol.CallResponse> attempt() async {
|
|
1723
1733
|
final requestNumber = ++plane._sequences.request;
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1734
|
+
final requestId = '$runtimeSession:offer:$requestNumber';
|
|
1735
|
+
final observer = NomosWrite.captureObserver();
|
|
1736
|
+
if (observer != null) plane._writeObservers[requestId] = observer;
|
|
1737
|
+
try {
|
|
1738
|
+
return await kernelTransport.callKernel(
|
|
1739
|
+
session: runtimeSession,
|
|
1740
|
+
request: nomos_protocol.CallRequest(
|
|
1741
|
+
protocolVersion: 1,
|
|
1742
|
+
requestId: requestId,
|
|
1743
|
+
offer: nomos_protocol.OfferRequest(
|
|
1744
|
+
call: nomos_protocol.DirectiveCall(
|
|
1745
|
+
domain: call.domain,
|
|
1746
|
+
directive: call.directive,
|
|
1747
|
+
operation: google_protobuf.Any(
|
|
1748
|
+
typeUrl: call.protobufType,
|
|
1749
|
+
value: call.protobufPayload,
|
|
1750
|
+
),
|
|
1736
1751
|
),
|
|
1752
|
+
authoring: nomos_protocol.AuthoringOptions(
|
|
1753
|
+
actor: actor ?? '',
|
|
1754
|
+
// Ordinary generated calls select a peer-runtime credential
|
|
1755
|
+
// capability below. An explicit secret is a temporary operator
|
|
1756
|
+
// compatibility lane and remains client-supplied until removed.
|
|
1757
|
+
authorSecret: explicitSecret == null
|
|
1758
|
+
? const <int>[]
|
|
1759
|
+
: base64Decode(explicitSecret),
|
|
1760
|
+
deferProjection: true,
|
|
1761
|
+
spans: true,
|
|
1762
|
+
),
|
|
1763
|
+
controllerLawHash: domainHash,
|
|
1764
|
+
conflictSnapshots: conflictSnapshots,
|
|
1765
|
+
birthCertificate: birthCertificate,
|
|
1737
1766
|
),
|
|
1738
|
-
authoring: nomos_protocol.AuthoringOptions(
|
|
1739
|
-
actor: actor ?? '',
|
|
1740
|
-
// Ordinary generated calls select a peer-runtime credential
|
|
1741
|
-
// capability below. An explicit secret is a temporary operator
|
|
1742
|
-
// compatibility lane and remains client-supplied until removed.
|
|
1743
|
-
authorSecret: explicitSecret == null
|
|
1744
|
-
? const <int>[]
|
|
1745
|
-
: base64Decode(explicitSecret),
|
|
1746
|
-
deferProjection: true,
|
|
1747
|
-
spans: true,
|
|
1748
|
-
),
|
|
1749
|
-
controllerLawHash: domainHash,
|
|
1750
|
-
conflictSnapshots: conflictSnapshots,
|
|
1751
|
-
birthCertificate: birthCertificate,
|
|
1752
1767
|
),
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1768
|
+
credentialUse: credentialUse,
|
|
1769
|
+
);
|
|
1770
|
+
} finally {
|
|
1771
|
+
plane._writeObservers.remove(requestId);
|
|
1772
|
+
observer?.call(const NomosWriteProgress(NomosWritePhase.running));
|
|
1773
|
+
}
|
|
1756
1774
|
}
|
|
1757
1775
|
|
|
1758
1776
|
var response = await attempt();
|
|
@@ -5995,7 +5995,7 @@ var CustodyBreakPolicySchema = /* @__PURE__ */ enumDesc(file_nomos_runtime_v2_re
|
|
|
5995
5995
|
var CustodyBreakPolicy = /* @__PURE__ */ tsEnum(CustodyBreakPolicySchema);
|
|
5996
5996
|
|
|
5997
5997
|
// ../../adapter/proto/nomos/runtime/v2/observability_desk_pb.js
|
|
5998
|
-
var file_nomos_runtime_v2_observability_desk = /* @__PURE__ */ fileDesc("Cilub21vcy9ydW50aW1lL3YyL29ic2VydmFiaWxpdHlfZGVzay5wcm90bxIQbm9tb3MucnVudGltZS52MiJCCgxUcmFjZUNvbnRleHQSEAoIdHJhY2VfaWQYASABKAwSDwoHc3Bhbl9pZBgCIAEoDBIPCgdzYW1wbGVkGAMgASgIIp0CCg5SdW50aW1lRmFpbHVyZRIMCgRjb2RlGAEgASgJEg8KB21lc3NhZ2UYAiABKAkSEQoJY29tcG9uZW50GAMgASgJEhYKCXdvcmtzcGFjZRgEIAEoCUgAiAEBEhMKBmRvbWFpbhgFIAEoCUgBiAEBEhYKCWRpcmVjdGl2ZRgGIAEoCUgCiAEBEjkKCmF0dHJpYnV0ZXMYByADKAsyJS5ub21vcy5ydW50aW1lLnYyLkRpYWdub3N0aWNBdHRyaWJ1dGUSMgoEa2luZBgIIAEoDjIkLm5vbW9zLnJ1bnRpbWUudjIuUnVudGltZUZhaWx1cmVLaW5kQgwKCl93b3Jrc3BhY2VCCQoHX2RvbWFpbkIMCgpfZGlyZWN0aXZlIr4BChJSdW50aW1lVW5hdmFpbGFibGUSOgoGcmVhc29uGAEgASgOMioubm9tb3MucnVudGltZS52Mi5SdW50aW1lVW5hdmFpbGFibGVSZWFzb24SEQoJd29ya3NwYWNlGAIgASgJEhEKCXJldHJ5YWJsZRgDIAEoCBIfChJyZXRyeV9hZnRlcl9taWxsaXMYBCABKA1IAIgBARIOCgZkZXRhaWwYBSABKAlCFQoTX3JldHJ5X2FmdGVyX21pbGxpcyJ+
|
|
5998
|
+
var file_nomos_runtime_v2_observability_desk = /* @__PURE__ */ fileDesc("Cilub21vcy9ydW50aW1lL3YyL29ic2VydmFiaWxpdHlfZGVzay5wcm90bxIQbm9tb3MucnVudGltZS52MiJCCgxUcmFjZUNvbnRleHQSEAoIdHJhY2VfaWQYASABKAwSDwoHc3Bhbl9pZBgCIAEoDBIPCgdzYW1wbGVkGAMgASgIIp0CCg5SdW50aW1lRmFpbHVyZRIMCgRjb2RlGAEgASgJEg8KB21lc3NhZ2UYAiABKAkSEQoJY29tcG9uZW50GAMgASgJEhYKCXdvcmtzcGFjZRgEIAEoCUgAiAEBEhMKBmRvbWFpbhgFIAEoCUgBiAEBEhYKCWRpcmVjdGl2ZRgGIAEoCUgCiAEBEjkKCmF0dHJpYnV0ZXMYByADKAsyJS5ub21vcy5ydW50aW1lLnYyLkRpYWdub3N0aWNBdHRyaWJ1dGUSMgoEa2luZBgIIAEoDjIkLm5vbW9zLnJ1bnRpbWUudjIuUnVudGltZUZhaWx1cmVLaW5kQgwKCl93b3Jrc3BhY2VCCQoHX2RvbWFpbkIMCgpfZGlyZWN0aXZlIr4BChJSdW50aW1lVW5hdmFpbGFibGUSOgoGcmVhc29uGAEgASgOMioubm9tb3MucnVudGltZS52Mi5SdW50aW1lVW5hdmFpbGFibGVSZWFzb24SEQoJd29ya3NwYWNlGAIgASgJEhEKCXJldHJ5YWJsZRgDIAEoCBIfChJyZXRyeV9hZnRlcl9taWxsaXMYBCABKA1IAIgBARIOCgZkZXRhaWwYBSABKAlCFQoTX3JldHJ5X2FmdGVyX21pbGxpcyJ+Cg5SdW50aW1lUmVmdXNhbBI2CgZyZWFzb24YASABKA4yJi5ub21vcy5ydW50aW1lLnYyLlJ1bnRpbWVSZWZ1c2FsUmVhc29uEhEKCXdvcmtzcGFjZRgCIAEoCRIRCglyZXRyeWFibGUYAyABKAgSDgoGZGV0YWlsGAQgASgJIqUBChNEaWFnbm9zdGljQXR0cmlidXRlEgwKBG5hbWUYASABKAkSFgoMc3RyaW5nX3ZhbHVlGAIgASgJSAASFwoNaW50ZWdlcl92YWx1ZRgDIAEoEkgAEhYKDGRvdWJsZV92YWx1ZRgEIAEoAUgAEhcKDWJvb2xlYW5fdmFsdWUYBSABKAhIABIVCgtieXRlc192YWx1ZRgGIAEoDEgAQgcKBXZhbHVlIqQLCgxSdW50aW1lRXZlbnQSEAoIc2VxdWVuY2UYASABKAQSFAoHc2Vzc2lvbhgCIAEoCUgBiAEBEi8KBXJlYWR5GAogASgLMh4ubm9tb3MucnVudGltZS52Mi5SdW50aW1lUmVhZHlIABI3Cg13YXRjaF9jaGFuZ2VkGAsgASgLMh4ubm9tb3MucnVudGltZS52Mi5XYXRjaENoYW5nZWRIABI5Cg5zeW5jX2NvbXBsZXRlZBgMIAEoCzIfLm5vbW9zLnJ1bnRpbWUudjIuU3luY0NvbXBsZXRlZEgAEjUKDGxpbmtfY2hhbmdlZBgNIAEoCzIdLm5vbW9zLnJ1bnRpbWUudjIuTGlua0NoYW5nZWRIABI5Cg5zaWduYWxfY2hhbmdlZBgOIAEoCzIfLm5vbW9zLnJ1bnRpbWUudjIuU2lnbmFsQ2hhbmdlZEgAEjsKD2N1c3RvZHlfY2hhbmdlZBgPIAEoCzIgLm5vbW9zLnJ1bnRpbWUudjIuQ3VzdG9keUNoYW5nZWRIABJCChNsb2NhbF93b3JrX3JlamVjdGVkGBAgASgLMiMubm9tb3MucnVudGltZS52Mi5Mb2NhbFdvcmtSZWplY3RlZEgAEjMKC2xhd19jaGFuZ2VkGBEgASgLMhwubm9tb3MucnVudGltZS52Mi5MYXdDaGFuZ2VkSAASQwoPa2VybmVsX3Byb2dyZXNzGBIgASgLMigubm9tb3MucnVudGltZS52Mi5LZXJuZWxQcm9ncmVzc09ic2VydmVkSAASLQoEc3BhbhgTIAEoCzIdLm5vbW9zLnJ1bnRpbWUudjIuUnVudGltZVNwYW5IABI/ChF0cmFuc2Zlcl9wcm9ncmVzcxgUIAEoCzIiLm5vbW9zLnJ1bnRpbWUudjIuVHJhbnNmZXJQcm9ncmVzc0gAEkkKFG1haW50ZW5hbmNlX3Byb2dyZXNzGBUgASgLMikubm9tb3MucnVudGltZS52Mi5SZWFkTWFpbnRlbmFuY2VQcm9ncmVzc0gAEjsKDXNvY2tldF9lZmZlY3QYFiABKAsyIi5ub21vcy5ydW50aW1lLnYyLlBlZXJTb2NrZXRFZmZlY3RIABJUChxjcmVkZW50aWFsX3JlZnJlc2hfcmVxdWVzdGVkGBcgASgLMiwubm9tb3MucnVudGltZS52Mi5DcmVkZW50aWFsUmVmcmVzaFJlcXVlc3RlZEgAElIKG3JlY2VpdmluZ19ncm91cF9jb252ZXJnZW5jZRgYIAEoCzIrLm5vbW9zLnJ1bnRpbWUudjIuUmVjZWl2aW5nR3JvdXBDb252ZXJnZW5jZUgAEkoKF21vZGVsX2Fkb3B0aW9uX3Byb2dyZXNzGBkgASgLMicubm9tb3MucnVudGltZS52Mi5Nb2RlbEFkb3B0aW9uUHJvZ3Jlc3NIABI7Cg9mb2xsb3dzX3N1bW1hcnkYGiABKAsyIC5ub21vcy5ydW50aW1lLnYyLkZvbGxvd3NTdW1tYXJ5SAASRAoUbG9jYWxfY29weV9wcmVzZXJ2ZWQYGyABKAsyJC5ub21vcy5ydW50aW1lLnYyLlByZXNlcnZlZExvY2FsQ29weUgAEk4KGWZvcmVpZ25fcGxhY2VtZW50X2lnbm9yZWQYHCABKAsyKS5ub21vcy5ydW50aW1lLnYyLkZvcmVpZ25QbGFjZW1lbnRJZ25vcmVkSAASQwoTYmluZGluZ19xdWFyYW50aW5lZBgdIAEoCzIkLm5vbW9zLnJ1bnRpbWUudjIuUXVhcmFudGluZWRCaW5kaW5nSAASPwoRd3JpdGVfcHJlcGFyYXRpb24YHiABKAsyIi5ub21vcy5ydW50aW1lLnYyLldyaXRlUHJlcGFyYXRpb25IAEIHCgVldmVudEIKCghfc2Vzc2lvbiK8AQoORm9sbG93c1N1bW1hcnkSEAoIZm9sbG93ZWQYASABKA0SDwoHc2V0dGxlZBgCIAEoDRIMCgRsaXZlGAMgASgNEjcKCndvcmtzcGFjZXMYBCADKAsyIy5ub21vcy5ydW50aW1lLnYyLkZvbGxvd09ic2VydmF0aW9uEiQKF29ic2VydmVkX2F0X3VuaXhfbWlsbGlzGAUgASgESACIAQFCGgoYX29ic2VydmVkX2F0X3VuaXhfbWlsbGlzItECChFGb2xsb3dPYnNlcnZhdGlvbhIRCgl3b3Jrc3BhY2UYASABKAkSNwoFcGhhc2UYAiABKA4yKC5ub21vcy5ydW50aW1lLnYyLkZvbGxvd09ic2VydmF0aW9uUGhhc2USDAoEbGl2ZRgDIAEoCBITCgZkZXRhaWwYBCABKAlIAIgBARIhChRyZXRyeV9hdF91bml4X21pbGxpcxgFIAEoBEgBiAEBEh8KEmR1ZV9hdF91bml4X21pbGxpcxgGIAEoBEgCiAEBEhQKB2F0dGVtcHQYByABKA1IA4gBARIaCg1hY3RpdmVfZWZmZWN0GAggASgJSASIAQFCCQoHX2RldGFpbEIXChVfcmV0cnlfYXRfdW5peF9taWxsaXNCFQoTX2R1ZV9hdF91bml4X21pbGxpc0IKCghfYXR0ZW1wdEIQCg5fYWN0aXZlX2VmZmVjdCJWChpDcmVkZW50aWFsUmVmcmVzaFJlcXVlc3RlZBI4CgdwZW5kaW5nGAEgASgLMicubm9tb3MucnVudGltZS52Mi5Db3JyZXNwb25kZW5jZVBlbmRpbmciPgoMUnVudGltZVJlYWR5EhcKD3J1bnRpbWVfdmVyc2lvbhgBIAEoCRIVCg1rZXJuZWxfc2hhMjU2GAIgASgMIvwBCgxXYXRjaENoYW5nZWQSFwoPc3Vic2NyaXB0aW9uX2lkGAEgASgJEhMKC3Jlc3VsdF91c2RhGAIgASgMEjYKB2ZhaWx1cmUYAyABKAsyIC5ub21vcy5ydW50aW1lLnYyLlJ1bnRpbWVGYWlsdXJlSACIAQESNQoSYXBwbGljYXRpb25fcmVzdWx0GAQgASgLMhQuZ29vZ2xlLnByb3RvYnVmLkFueUgBiAEBEhoKDWludGVnZXJfdmFsdWUYBSABKANIAogBAUIKCghfZmFpbHVyZUIVChNfYXBwbGljYXRpb25fcmVzdWx0QhAKDl9pbnRlZ2VyX3ZhbHVlIpwBCg1TeW5jQ29tcGxldGVkEg4KBnJlYXNvbhgBIAEoCRI3CgZyZXN1bHQYAiABKAsyJy5ub21vcy5ydW50aW1lLnYyLlN5bmNXb3Jrc3BhY2VSZXNwb25zZRI2CgdmYWlsdXJlGAMgASgLMiAubm9tb3MucnVudGltZS52Mi5SdW50aW1lRmFpbHVyZUgAiAEBQgoKCF9mYWlsdXJlIjsKC0xpbmtDaGFuZ2VkEiwKBnN0YXR1cxgBIAEoDjIcLm5vbW9zLnJ1bnRpbWUudjIuTGlua1N0YXR1cyI4Cg1TaWduYWxDaGFuZ2VkEg4KBmRvbWFpbhgBIAEoCRIXCg9wcm9qZWN0aW9uX3VzZGEYAiABKAwiUAoKTGF3Q2hhbmdlZBIOCgZkb21haW4YASABKAkSFgoOcGFja2FnZV9zaGEyNTYYAiABKAkSGgoSY29tcGF0aWJpbGl0eV91c2RhGAMgASgMIt0BCgtSdW50aW1lU3BhbhIQCgh0cmFjZV9pZBgBIAEoDBIPCgdzcGFuX2lkGAIgASgMEhsKDnBhcmVudF9zcGFuX2lkGAMgASgMSACIAQESDAoEbmFtZRgEIAEoCRIaChJzdGFydGVkX3VuaXhfbmFub3MYBSABKAQSFgoOZHVyYXRpb25fbmFub3MYBiABKAQSOQoKYXR0cmlidXRlcxgHIAMoCzIlLm5vbW9zLnJ1bnRpbWUudjIuRGlhZ25vc3RpY0F0dHJpYnV0ZUIRCg9fcGFyZW50X3NwYW5faWQi6gEKFktlcm5lbFByb2dyZXNzT2JzZXJ2ZWQSOQoIc25hcHNob3QYASABKAsyJy5ub21vcy5rZXJuZWwudjEuS2VybmVsUHJvZ3Jlc3NTbmFwc2hvdBIkChxob3N0X29ic2VydmVkX2VsYXBzZWRfbWljcm9zGAIgASgEEioKImhvc3Rfb2JzZXJ2ZWRfcGhhc2VfZWxhcHNlZF9taWNyb3MYAyABKAQSHgoWcHJvY2Vzc19yZXNpZGVudF9ieXRlcxgEIAEoBBIjChtwcm9jZXNzX3BlYWtfcmVzaWRlbnRfYnl0ZXMYBSABKAQiggEKEFRyYW5zZmVyUHJvZ3Jlc3MSFgoOcmVjZWl2ZWRfYnl0ZXMYASABKAQSGAoLdG90YWxfYnl0ZXMYAiABKARIAIgBARIsCgRraW5kGAMgASgOMh4ubm9tb3MucnVudGltZS52Mi5UcmFuc2ZlcktpbmRCDgoMX3RvdGFsX2J5dGVzIjkKEFdyaXRlUHJlcGFyYXRpb24SEgoKcmVxdWVzdF9pZBgBIAEoCRIRCgl3b3Jrc3BhY2UYAiABKAkqygEKElJ1bnRpbWVGYWlsdXJlS2luZBIkCiBSVU5USU1FX0ZBSUxVUkVfS0lORF9VTlNQRUNJRklFRBAAEiEKHVJVTlRJTUVfRkFJTFVSRV9LSU5EX0lOVEVSTkFMEAESIQodUlVOVElNRV9GQUlMVVJFX0tJTkRfUFJPVE9DT0wQAhIiCh5SVU5USU1FX0ZBSUxVUkVfS0lORF9JTlRFR1JJVFkQAxIkCiBSVU5USU1FX0ZBSUxVUkVfS0lORF9VTlNVUFBPUlRFRBAEKqcDChhSdW50aW1lVW5hdmFpbGFibGVSZWFzb24SKgomUlVOVElNRV9VTkFWQUlMQUJMRV9SRUFTT05fVU5TUEVDSUZJRUQQABIyCi5SVU5USU1FX1VOQVZBSUxBQkxFX1JFQVNPTl9SRU1PVEVfT05MWV9PRkZMSU5FEAESMQotUlVOVElNRV9VTkFWQUlMQUJMRV9SRUFTT05fUkVNT1RFX1VOUkVBQ0hBQkxFEAISLQopUlVOVElNRV9VTkFWQUlMQUJMRV9SRUFTT05fUkVNT1RFX1RJTUVPVVQQAxI2CjJSVU5USU1FX1VOQVZBSUxBQkxFX1JFQVNPTl9MT0NBTF9SRVBMSUNBX05PVF9SRUFEWRAEEjUKMVJVTlRJTUVfVU5BVkFJTEFCTEVfUkVBU09OX0NSRURFTlRJQUxfVU5BVkFJTEFCTEUQBRIwCixSVU5USU1FX1VOQVZBSUxBQkxFX1JFQVNPTl9XT1JLU1BBQ0VfT1BFTklORxAGEigKJFJVTlRJTUVfVU5BVkFJTEFCTEVfUkVBU09OX1BFRVJfQlVTWRAHKvABChRSdW50aW1lUmVmdXNhbFJlYXNvbhImCiJSVU5USU1FX1JFRlVTQUxfUkVBU09OX1VOU1BFQ0lGSUVEEAASLQopUlVOVElNRV9SRUZVU0FMX1JFQVNPTl9JTlZBTElEX0NSRURFTlRJQUwQARIoCiRSVU5USU1FX1JFRlVTQUxfUkVBU09OX0FDQ0VTU19ERU5JRUQQAhIrCidSVU5USU1FX1JFRlVTQUxfUkVBU09OX1BMQUNFTUVOVF9QT0xJQ1kQAxIqCiZSVU5USU1FX1JFRlVTQUxfUkVBU09OX1NFU1NJT05fVU5CT1VORBAEKpUEChZGb2xsb3dPYnNlcnZhdGlvblBoYXNlEigKJEZPTExPV19PQlNFUlZBVElPTl9QSEFTRV9VTlNQRUNJRklFRBAAEiQKIEZPTExPV19PQlNFUlZBVElPTl9QSEFTRV9ERVNJUkVEEAESIAocRk9MTE9XX09CU0VSVkFUSU9OX1BIQVNFX0RVRRACEiQKIEZPTExPV19PQlNFUlZBVElPTl9QSEFTRV9SVU5OSU5HEAMSJAogRk9MTE9XX09CU0VSVkFUSU9OX1BIQVNFX1NFVFRMRUQQBBIwCixGT0xMT1dfT0JTRVJWQVRJT05fUEhBU0VfQVdBSVRJTkdfREVQRU5ERU5DWRAFEiQKIEZPTExPV19PQlNFUlZBVElPTl9QSEFTRV9PRkZMSU5FEAYSLworRk9MTE9XX09CU0VSVkFUSU9OX1BIQVNFX0lOVkFMSURfQ1JFREVOVElBTBAHEiQKIEZPTExPV19PQlNFUlZBVElPTl9QSEFTRV9CQUNLT0ZGEAgSLAooRk9MTE9XX09CU0VSVkFUSU9OX1BIQVNFX1JFQk9STl9SRU1PVEVMWRAJEiwKKEZPTExPV19PQlNFUlZBVElPTl9QSEFTRV9SRVBMQUNJTkdfTE9DQUwQChIyCi5GT0xMT1dfT0JTRVJWQVRJT05fUEhBU0VfTE9DQUxfQ09QWV9VTlJFQURBQkxFEAsqVwoKTGlua1N0YXR1cxIbChdMSU5LX1NUQVRVU19VTlNQRUNJRklFRBAAEhQKEExJTktfU1RBVFVTX09QRU4QARIWChJMSU5LX1NUQVRVU19DTE9TRUQQAip2CgxUcmFuc2ZlcktpbmQSHQoZVFJBTlNGRVJfS0lORF9VTlNQRUNJRklFRBAAEhcKE1RSQU5TRkVSX0tJTkRfREVMVEEQARIWChJUUkFOU0ZFUl9LSU5EX0ZVTEwQAhIWChJUUkFOU0ZFUl9LSU5EX1BVU0gQA2IGcHJvdG8z", [file_google_protobuf_any, file_nomos_kernel_v1_progress, file_nomos_runtime_v2_correspondence_desk, file_nomos_runtime_v2_custody_desk, file_nomos_runtime_v2_placement_desk, file_nomos_runtime_v2_maintenance_desk, file_nomos_runtime_v2_model_adoption_desk, file_nomos_runtime_v2_receiving_group_desk, file_nomos_runtime_v2_recovery_desk]);
|
|
5999
5999
|
var RuntimeFailureSchema = /* @__PURE__ */ messageDesc(file_nomos_runtime_v2_observability_desk, 1);
|
|
6000
6000
|
var RuntimeUnavailableSchema = /* @__PURE__ */ messageDesc(file_nomos_runtime_v2_observability_desk, 2);
|
|
6001
6001
|
var RuntimeRefusalSchema = /* @__PURE__ */ messageDesc(file_nomos_runtime_v2_observability_desk, 3);
|
|
@@ -6009,6 +6009,7 @@ var WatchChangedSchema = /* @__PURE__ */ messageDesc(file_nomos_runtime_v2_obser
|
|
|
6009
6009
|
var SyncCompletedSchema = /* @__PURE__ */ messageDesc(file_nomos_runtime_v2_observability_desk, 11);
|
|
6010
6010
|
var LinkChangedSchema = /* @__PURE__ */ messageDesc(file_nomos_runtime_v2_observability_desk, 12);
|
|
6011
6011
|
var RuntimeSpanSchema = /* @__PURE__ */ messageDesc(file_nomos_runtime_v2_observability_desk, 15);
|
|
6012
|
+
var WritePreparationSchema = /* @__PURE__ */ messageDesc(file_nomos_runtime_v2_observability_desk, 18);
|
|
6012
6013
|
var RuntimeFailureKindSchema = /* @__PURE__ */ enumDesc(file_nomos_runtime_v2_observability_desk, 0);
|
|
6013
6014
|
var RuntimeFailureKind = /* @__PURE__ */ tsEnum(RuntimeFailureKindSchema);
|
|
6014
6015
|
var RuntimeUnavailableReasonSchema = /* @__PURE__ */ enumDesc(file_nomos_runtime_v2_observability_desk, 1);
|
|
@@ -16356,6 +16357,18 @@ var CredentialSessions = class {
|
|
|
16356
16357
|
// ../src/runtime/credential_recovery_desk.ts
|
|
16357
16358
|
init_define_NOMOS_KERNEL_RELEASE();
|
|
16358
16359
|
|
|
16360
|
+
// ../src/runtime/credential_failure.ts
|
|
16361
|
+
init_define_NOMOS_KERNEL_RELEASE();
|
|
16362
|
+
function recoverableCredential(failure) {
|
|
16363
|
+
return failure.code === "nomos.auth.token_expired" || failure.code === "nomos.auth.token_missing";
|
|
16364
|
+
}
|
|
16365
|
+
function syncNeedsCredentialRefresh(result, failure) {
|
|
16366
|
+
if (failure !== void 0 && recoverableCredential(failure)) return true;
|
|
16367
|
+
const offer = result?.offerReceipt?.outcome;
|
|
16368
|
+
const pushNeedsRefresh = offer?.case === "failure" ? recoverableCredential(offer.value) : result?.pushError?.status === 401;
|
|
16369
|
+
return pushNeedsRefresh || result?.discoveryError?.status === 401;
|
|
16370
|
+
}
|
|
16371
|
+
|
|
16359
16372
|
// ../src/runtime/credential_renewal.ts
|
|
16360
16373
|
init_define_NOMOS_KERNEL_RELEASE();
|
|
16361
16374
|
var RENEW_BEFORE_MILLIS = 5 * 6e4;
|
|
@@ -16621,7 +16634,7 @@ var CredentialRecoveryDesk = class {
|
|
|
16621
16634
|
return false;
|
|
16622
16635
|
}
|
|
16623
16636
|
observeSync(session, event) {
|
|
16624
|
-
if (!syncNeedsCredentialRefresh(event) || this.#tokenless(session)) return;
|
|
16637
|
+
if (!syncNeedsCredentialRefresh(event.result, event.failure) || this.#tokenless(session)) return;
|
|
16625
16638
|
this.#prune();
|
|
16626
16639
|
const capability = this.#ports.capabilityFor(session);
|
|
16627
16640
|
const existing = this.#active.get(capability);
|
|
@@ -16823,15 +16836,6 @@ var CredentialRecoveryDesk = class {
|
|
|
16823
16836
|
this.#completed.clear();
|
|
16824
16837
|
}
|
|
16825
16838
|
};
|
|
16826
|
-
function recoverableCredential(failure) {
|
|
16827
|
-
return failure.code === "nomos.auth.token_expired" || failure.code === "nomos.auth.token_missing";
|
|
16828
|
-
}
|
|
16829
|
-
function syncNeedsCredentialRefresh(event) {
|
|
16830
|
-
const failures = [event.result?.pushError, event.result?.discoveryError];
|
|
16831
|
-
if (failures.some((failure) => failure?.status === 401 || failure?.status === 403)) return true;
|
|
16832
|
-
const text = `${event.failure?.message ?? ""} ${failures.map((failure) => failure?.message ?? "").join(" ")}`.toLowerCase();
|
|
16833
|
-
return text.includes("unauthenticated") || text.includes("token expired") || text.includes("invalid token");
|
|
16834
|
-
}
|
|
16835
16839
|
|
|
16836
16840
|
// ../src/runtime/device_identity_desk.ts
|
|
16837
16841
|
init_define_NOMOS_KERNEL_RELEASE();
|
|
@@ -17750,6 +17754,7 @@ var ReceivingGroupDesk = class {
|
|
|
17750
17754
|
#accounts = /* @__PURE__ */ new Map();
|
|
17751
17755
|
/** D22: device key hash → the workspaces the console reported this key enrolled in (this process's memory, not truth). */
|
|
17752
17756
|
#consoleEnrolments = /* @__PURE__ */ new Map();
|
|
17757
|
+
#preparingWrites = /* @__PURE__ */ new Map();
|
|
17753
17758
|
#sequence = 0;
|
|
17754
17759
|
#internalRequestId = 30000000000n;
|
|
17755
17760
|
constructor(ports) {
|
|
@@ -17789,16 +17794,124 @@ var ReceivingGroupDesk = class {
|
|
|
17789
17794
|
if (head === account.observedHead) return;
|
|
17790
17795
|
this.#tails.set(workspace, { ...account, inFlight: false, attempts: 0, notBefore: 0n });
|
|
17791
17796
|
}
|
|
17792
|
-
/**
|
|
17793
|
-
*
|
|
17797
|
+
/** Only a proven non-admission can enter recovery. Console-enrolled children
|
|
17798
|
+
* share the owning application's route, selected by credential capability and
|
|
17799
|
+
* principal, never by workspace-name guessing or an unverified payload. */
|
|
17794
17800
|
recoverOffer(request, response) {
|
|
17795
|
-
if (!this.#ports.call || request.command.case !== "kernelCall" || request.command.value.credentialUse !== CredentialUse.SESSION || request.command.value.request?.operation.case !== "offer" || request.command.value.request.operation.value.intent.case !== "call"
|
|
17801
|
+
if (!this.#ports.call || request.command.case !== "kernelCall" || request.command.value.credentialUse !== CredentialUse.SESSION || request.command.value.request?.operation.case !== "offer" || request.command.value.request.operation.value.intent.case !== "call") return;
|
|
17802
|
+
const missing = response.outcome.case === "failure" ? response.outcome.value.code === "nomos.authz.signer_enrollment_missing" : response.outcome.case === "offer" && response.outcome.value.outcome.case === "refused" && response.outcome.value.outcome.value.refusal?.code === KernelRefusalCode.FORGED_PRINCIPAL && response.outcome.value.outcome.value.refusal.details?.fields.actor?.kind.case === "text" && response.outcome.value.outcome.value.refusal.details.fields.actor.kind.value === "unenrolled-signer-key";
|
|
17803
|
+
const revoked = response.outcome.case === "failure" && response.outcome.value.code === "nomos.authz.device_key_revoked";
|
|
17804
|
+
if (!missing && !revoked) return;
|
|
17796
17805
|
const binding = this.#ports.binding(request.command.value.session);
|
|
17797
|
-
|
|
17798
|
-
|
|
17806
|
+
if (!binding) return;
|
|
17807
|
+
const enrolled = [...this.#accounts.values()].find((account2) => {
|
|
17808
|
+
const owner = this.#ports.binding(account2.session);
|
|
17809
|
+
return account2.enrolment && owner?.credentialCapability === binding.credentialCapability && owner.principal === binding.principal;
|
|
17810
|
+
});
|
|
17799
17811
|
const copy3 = fromBinary(RuntimeRequestSchema, toBinary(RuntimeRequestSchema, request));
|
|
17812
|
+
if (enrolled?.enrolment) {
|
|
17813
|
+
if (revoked) return this.#writeRefused(binding.workspace, "This device's access has been revoked.");
|
|
17814
|
+
return this.#begin(request, `enrolled-write-${++this.#sequence}`, (workId2) => this.#prepareEnrolledWrite(copy3, binding, enrolled.application, enrolled.enrolment, workId2));
|
|
17815
|
+
}
|
|
17816
|
+
const account = this.#accounts.get(binding.workspace);
|
|
17817
|
+
if (!account) return;
|
|
17800
17818
|
return this.#begin(request, `receiving-group-authoring-${++this.#sequence}`, (workId2) => this.#recoverOffer(copy3, response, account, workId2));
|
|
17801
17819
|
}
|
|
17820
|
+
#writeRefused(workspace, detail, reason = RuntimeRefusalReason.ACCESS_DENIED) {
|
|
17821
|
+
return { case: "refused", value: create(RuntimeRefusalSchema, {
|
|
17822
|
+
reason,
|
|
17823
|
+
workspace,
|
|
17824
|
+
retryable: false,
|
|
17825
|
+
detail
|
|
17826
|
+
}) };
|
|
17827
|
+
}
|
|
17828
|
+
#writeWaiting(workspace, detail) {
|
|
17829
|
+
return { case: "unavailable", value: create(RuntimeUnavailableSchema, {
|
|
17830
|
+
reason: RuntimeUnavailableReason.CREDENTIAL_UNAVAILABLE,
|
|
17831
|
+
workspace,
|
|
17832
|
+
retryable: true,
|
|
17833
|
+
retryAfterMillis: 1e3,
|
|
17834
|
+
detail
|
|
17835
|
+
}) };
|
|
17836
|
+
}
|
|
17837
|
+
*#prepareEnrolledWrite(request, binding, application, route, workId2) {
|
|
17838
|
+
if (request.command.case !== "kernelCall") throw new Error("write preparation lost its call");
|
|
17839
|
+
const command = request.command.value;
|
|
17840
|
+
const principal = binding.principal;
|
|
17841
|
+
const unavailable = () => this.#writeWaiting(
|
|
17842
|
+
binding.workspace,
|
|
17843
|
+
"This device is still being prepared to save changes. Your change has not been saved. Try again when connected."
|
|
17844
|
+
);
|
|
17845
|
+
this.#ports.emit(binding.session, { case: "writePreparation", value: create(WritePreparationSchema, {
|
|
17846
|
+
requestId: command.request.requestId,
|
|
17847
|
+
workspace: binding.workspace
|
|
17848
|
+
}) });
|
|
17849
|
+
const key = JSON.stringify([binding.credentialCapability, principal, binding.workspace]);
|
|
17850
|
+
const shared = this.#preparingWrites.get(key);
|
|
17851
|
+
const deadline = this.#ports.clock.monotonicNanoseconds() + 30000000000n;
|
|
17852
|
+
let wake = 0;
|
|
17853
|
+
if (shared) {
|
|
17854
|
+
while (!shared.settled) {
|
|
17855
|
+
if (this.#ports.clock.monotonicNanoseconds() >= deadline) return unavailable();
|
|
17856
|
+
const receipt = yield { case: "wake", value: create(PeerWakeEffectSchema, {
|
|
17857
|
+
effectId: `${workId2}:join-${++wake}`,
|
|
17858
|
+
delayMillis: 250
|
|
17859
|
+
}) };
|
|
17860
|
+
if (receipt.outcome.case !== "woken") return unavailable();
|
|
17861
|
+
}
|
|
17862
|
+
if (shared.result) return shared.result;
|
|
17863
|
+
} else {
|
|
17864
|
+
const preparation = { settled: false };
|
|
17865
|
+
this.#preparingWrites.set(key, preparation);
|
|
17866
|
+
try {
|
|
17867
|
+
const secret = this.#ports.authorSecretIfPresent(binding.session);
|
|
17868
|
+
if (!secret) return preparation.result = this.#writeRefused(binding.workspace, "Sign in again to set up this device.", RuntimeRefusalReason.INVALID_CREDENTIAL);
|
|
17869
|
+
const keyHash = sha256Hex(this.#ports.deriveSigningPublicKey(secret));
|
|
17870
|
+
let posture = this.#ports.posture(request, binding.session, principal, secret, false);
|
|
17871
|
+
if (posture.deviceSignerStatus === DeviceSignerStatus.REVOKED) {
|
|
17872
|
+
return preparation.result = this.#writeRefused(binding.workspace, "This device's access has been revoked.");
|
|
17873
|
+
}
|
|
17874
|
+
if (!posture.enrolledDevice || posture.deviceSignerStatus !== DeviceSignerStatus.ACTIVE) {
|
|
17875
|
+
const enrolled = yield* this.#enrolAtConsole(
|
|
17876
|
+
route,
|
|
17877
|
+
binding,
|
|
17878
|
+
`${application}|${principal}`,
|
|
17879
|
+
principal,
|
|
17880
|
+
keyHash,
|
|
17881
|
+
[binding.workspace],
|
|
17882
|
+
`${workId2}:console`
|
|
17883
|
+
);
|
|
17884
|
+
if (enrolled.case === "deferred") return preparation.result = enrolled.credentialRefused ? this.#writeRefused(binding.workspace, "Sign in again to set up this device.", RuntimeRefusalReason.INVALID_CREDENTIAL) : unavailable();
|
|
17885
|
+
if (!(binding.workspace in enrolled.value.heads)) {
|
|
17886
|
+
return preparation.result = this.#writeRefused(binding.workspace, "This device could not obtain access to save changes in this workspace.");
|
|
17887
|
+
}
|
|
17888
|
+
while (true) {
|
|
17889
|
+
posture = this.#ports.posture(request, binding.session, principal, secret, false);
|
|
17890
|
+
if (posture.deviceSignerStatus === DeviceSignerStatus.REVOKED) {
|
|
17891
|
+
return preparation.result = this.#writeRefused(binding.workspace, "This device's access has been revoked.");
|
|
17892
|
+
}
|
|
17893
|
+
if (posture.enrolledDevice && posture.deviceSignerStatus === DeviceSignerStatus.ACTIVE) break;
|
|
17894
|
+
if (this.#ports.clock.monotonicNanoseconds() >= deadline) return preparation.result = unavailable();
|
|
17895
|
+
const receipt = yield { case: "wake", value: create(PeerWakeEffectSchema, {
|
|
17896
|
+
effectId: `${workId2}:observe-${++wake}`,
|
|
17897
|
+
delayMillis: 500
|
|
17898
|
+
}) };
|
|
17899
|
+
if (receipt.outcome.case !== "woken") return preparation.result = unavailable();
|
|
17900
|
+
}
|
|
17901
|
+
}
|
|
17902
|
+
} catch (error) {
|
|
17903
|
+
if (error instanceof ReceivingGroupRefusal) {
|
|
17904
|
+
return preparation.result = this.#writeRefused(binding.workspace, "This device could not obtain access to save changes in this workspace.");
|
|
17905
|
+
}
|
|
17906
|
+
preparation.result = unavailable();
|
|
17907
|
+
throw error;
|
|
17908
|
+
} finally {
|
|
17909
|
+
preparation.settled = true;
|
|
17910
|
+
this.#preparingWrites.delete(key);
|
|
17911
|
+
}
|
|
17912
|
+
}
|
|
17913
|
+
return { case: "retried", request, response: this.#ports.call(request, command) };
|
|
17914
|
+
}
|
|
17802
17915
|
*#recoverOffer(request, response, account, workId2) {
|
|
17803
17916
|
const workspace = this.#ports.binding(account.session).workspace;
|
|
17804
17917
|
const deadline = this.#ports.clock.monotonicNanoseconds() + 60000000000n;
|
|
@@ -17866,6 +17979,7 @@ var ReceivingGroupDesk = class {
|
|
|
17866
17979
|
this.#opening.clear();
|
|
17867
17980
|
this.#accounts.clear();
|
|
17868
17981
|
this.#consoleEnrolments.clear();
|
|
17982
|
+
this.#preparingWrites.clear();
|
|
17869
17983
|
}
|
|
17870
17984
|
#begin(request, workId2, procedure) {
|
|
17871
17985
|
const held = {
|
|
@@ -21821,12 +21935,12 @@ var ReplicaFollowController = class {
|
|
|
21821
21935
|
this.#desk.transition(result.workspace, { case: ReplicaFollowEventKind.WentOffline, nowUnixMillis });
|
|
21822
21936
|
return delayUntilDue(this.#desk.get(result.workspace), nowUnixMillis);
|
|
21823
21937
|
}
|
|
21824
|
-
|
|
21825
|
-
|
|
21938
|
+
if (syncNeedsCredentialRefresh(result)) {
|
|
21939
|
+
const offer = result.offerReceipt?.outcome;
|
|
21826
21940
|
const transition = this.#desk.transition(result.workspace, {
|
|
21827
21941
|
case: ReplicaFollowEventKind.CredentialRejected,
|
|
21828
21942
|
nowUnixMillis,
|
|
21829
|
-
failure:
|
|
21943
|
+
failure: (offer?.case === "failure" ? offer.value.message : void 0) || result.pushError?.message || result.discoveryError?.message || "remote requires a refreshed credential"
|
|
21830
21944
|
});
|
|
21831
21945
|
if (transition.effects.some((effect) => effect.case === ReplicaFollowEffectKind.RefreshCredential)) {
|
|
21832
21946
|
return 0;
|
|
@@ -30152,6 +30266,7 @@ var PeerOffice = class {
|
|
|
30152
30266
|
return this.#responses.modelAdoption(request, step);
|
|
30153
30267
|
}
|
|
30154
30268
|
#receivingGroupResponse(request, step) {
|
|
30269
|
+
if (step.case === "refused") return this.#response(request, { case: "refused", value: step.value });
|
|
30155
30270
|
if (step.case === "retried") return this.#birthCustodyResponse(step.request, this.#birthCustody.afterKernelCall(step.request, step.response));
|
|
30156
30271
|
if (step.case === "pending") return this.#response(request, { case: "correspondencePending", value: step.value });
|
|
30157
30272
|
if (step.case === "failure") return this.#failure(request, step.code, step.message);
|
|
@@ -7,7 +7,6 @@ import 'package:nomos_types/nomos_kernel_protocol.dart' as nomos_protocol;
|
|
|
7
7
|
import 'package:nomos_types/nomos_types.dart'
|
|
8
8
|
show
|
|
9
9
|
NomosErrorInfo,
|
|
10
|
-
NomosRefusalReason,
|
|
11
10
|
NomosRefused,
|
|
12
11
|
NomosRuntimeFailure,
|
|
13
12
|
NomosRuntimeFailureKind,
|
|
@@ -46,27 +45,7 @@ final class NmsRuntimeV2Gateway {
|
|
|
46
45
|
throw NomosUnavailable.fromRuntime(response.unavailable);
|
|
47
46
|
}
|
|
48
47
|
if (response.hasRefused()) {
|
|
49
|
-
|
|
50
|
-
throw NomosRefused(
|
|
51
|
-
reason: switch (refused.reason) {
|
|
52
|
-
nomos_protocol.RuntimeRefusalReason
|
|
53
|
-
.RUNTIME_REFUSAL_REASON_INVALID_CREDENTIAL =>
|
|
54
|
-
NomosRefusalReason.invalidCredential,
|
|
55
|
-
nomos_protocol
|
|
56
|
-
.RuntimeRefusalReason.RUNTIME_REFUSAL_REASON_ACCESS_DENIED =>
|
|
57
|
-
NomosRefusalReason.accessDenied,
|
|
58
|
-
nomos_protocol
|
|
59
|
-
.RuntimeRefusalReason.RUNTIME_REFUSAL_REASON_PLACEMENT_POLICY =>
|
|
60
|
-
NomosRefusalReason.placementPolicy,
|
|
61
|
-
nomos_protocol
|
|
62
|
-
.RuntimeRefusalReason.RUNTIME_REFUSAL_REASON_SESSION_UNBOUND =>
|
|
63
|
-
NomosRefusalReason.sessionUnbound,
|
|
64
|
-
_ => NomosRefusalReason.unknown,
|
|
65
|
-
},
|
|
66
|
-
workspace: refused.workspace,
|
|
67
|
-
retryable: refused.retryable,
|
|
68
|
-
detail: refused.detail,
|
|
69
|
-
);
|
|
48
|
+
throw NomosRefused.fromRuntime(response.refused);
|
|
70
49
|
}
|
|
71
50
|
if (response.hasFailure()) {
|
|
72
51
|
final failure = response.failure;
|
|
@@ -16,7 +16,8 @@ import 'package:nomos_peer_host/nomos_peer_host.dart'
|
|
|
16
16
|
NomosProviderCredentialStore,
|
|
17
17
|
NomosSignIn;
|
|
18
18
|
import 'package:nomos_types/nomos_kernel_protocol.dart' as nomos_protocol;
|
|
19
|
-
import 'package:nomos_types/nomos_types.dart'
|
|
19
|
+
import 'package:nomos_types/nomos_types.dart'
|
|
20
|
+
show NomosUnavailable, NomosRefused;
|
|
20
21
|
|
|
21
22
|
import 'console_enrolment.dart';
|
|
22
23
|
import 'console_http_stub.dart' if (dart.library.io) 'console_http_io.dart'
|
|
@@ -648,6 +649,9 @@ mixin NomosRuntimeV2KernelCalls
|
|
|
648
649
|
if (response.hasUnavailable()) {
|
|
649
650
|
throw NomosUnavailable.fromRuntime(response.unavailable);
|
|
650
651
|
}
|
|
652
|
+
if (response.hasRefused()) {
|
|
653
|
+
throw NomosRefused.fromRuntime(response.refused);
|
|
654
|
+
}
|
|
651
655
|
if (!response.hasKernelCall()) {
|
|
652
656
|
throw StateError(
|
|
653
657
|
'Runtime v2 returned ${response.whichOutcome().name} for a kernel call',
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import 'dart:async';
|
|
2
|
+
|
|
3
|
+
/// Presentation of one application action. Completion is the action's local
|
|
4
|
+
/// result; remote durability remains on its existing operation receipt.
|
|
5
|
+
enum NomosWritePhase { running, preparing, completed, failed }
|
|
6
|
+
|
|
7
|
+
final class NomosWriteProgress {
|
|
8
|
+
const NomosWriteProgress(this.phase);
|
|
9
|
+
final NomosWritePhase phase;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/// Optional observation around a generated business action, including any
|
|
13
|
+
/// prerequisite calls it makes. Ordinary unobserved calls recover identically.
|
|
14
|
+
/// This holds no queue, credentials, authority or retry policy. It does not
|
|
15
|
+
/// persist a pending action across process death or cancel an accepted write.
|
|
16
|
+
abstract final class NomosWrite {
|
|
17
|
+
static final Object _observerKey = Object();
|
|
18
|
+
|
|
19
|
+
static Future<T> observe<T>(
|
|
20
|
+
Future<T> Function() action, {
|
|
21
|
+
required void Function(NomosWriteProgress) onProgress,
|
|
22
|
+
}) async {
|
|
23
|
+
void report(NomosWritePhase phase) {
|
|
24
|
+
try {
|
|
25
|
+
onProgress(NomosWriteProgress(phase));
|
|
26
|
+
} catch (_) {
|
|
27
|
+
// Presentation cannot change the operation's outcome.
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
report(NomosWritePhase.running);
|
|
32
|
+
try {
|
|
33
|
+
final result = await runZoned(action, zoneValues: {
|
|
34
|
+
_observerKey: (NomosWriteProgress progress) => report(progress.phase),
|
|
35
|
+
});
|
|
36
|
+
report(NomosWritePhase.completed);
|
|
37
|
+
return result;
|
|
38
|
+
} catch (_) {
|
|
39
|
+
report(NomosWritePhase.failed);
|
|
40
|
+
rethrow;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/// Framework projection seam. Capture before dispatch so an event delivered
|
|
45
|
+
/// outside the calling zone still reaches only its own action's observer.
|
|
46
|
+
static void Function(NomosWriteProgress)? captureObserver() =>
|
|
47
|
+
Zone.current[_observerKey] as void Function(NomosWriteProgress)?;
|
|
48
|
+
}
|
package/dart/pubspec.lock
CHANGED
package/dart/pubspec.yaml
CHANGED
|
@@ -7,7 +7,7 @@ repository: https://github.com/Captain-App/nomos2
|
|
|
7
7
|
description: >-
|
|
8
8
|
Nomos 2 Dart client — generated domain values, intent payloads and application
|
|
9
9
|
lifecycle types over the typed Nomos kernel protocol and USDA custody model.
|
|
10
|
-
version: 0.66.
|
|
10
|
+
version: 0.66.9
|
|
11
11
|
|
|
12
12
|
environment:
|
|
13
13
|
sdk: ^3.5.0
|
|
@@ -18,7 +18,7 @@ dependencies:
|
|
|
18
18
|
# The pure-data core (wire types, offer codec, NomosReads, schema validation, error
|
|
19
19
|
# info). nomos_client depends on and re-exports it, so existing imports are unchanged.
|
|
20
20
|
# For local monorepo dev, pubspec_overrides.yaml points this at ../dart_types.
|
|
21
|
-
nomos_types: ^0.11.
|
|
21
|
+
nomos_types: ^0.11.3
|
|
22
22
|
fixnum: ^1.1.1
|
|
23
23
|
json_schema: ^5.2.2
|
|
24
24
|
crypto: ^3.0.0
|
|
@@ -3,6 +3,7 @@ import 'dart:convert';
|
|
|
3
3
|
import 'dart:typed_data';
|
|
4
4
|
|
|
5
5
|
import 'package:nomos_client/src/nms_generated_runtime.dart';
|
|
6
|
+
import 'package:nomos_client/src/write_progress.dart';
|
|
6
7
|
import 'package:nomos_types/nomos_kernel_protocol.dart' as protocol;
|
|
7
8
|
import 'package:test/test.dart';
|
|
8
9
|
|
|
@@ -22,6 +23,7 @@ final class _RepairTransport
|
|
|
22
23
|
var syncCalls = 0;
|
|
23
24
|
final syncSessions = <String>[];
|
|
24
25
|
var offerCalls = 0;
|
|
26
|
+
Completer<void>? holdOffer;
|
|
25
27
|
var expireNextSessionOffer = false;
|
|
26
28
|
var refuseSignerEnrollmentOffers = 0;
|
|
27
29
|
var refuseKernelSignerOffers = 0;
|
|
@@ -142,6 +144,14 @@ final class _RepairTransport
|
|
|
142
144
|
}
|
|
143
145
|
if (request.hasOffer()) {
|
|
144
146
|
offerCalls += 1;
|
|
147
|
+
if (holdOffer != null) {
|
|
148
|
+
pushRuntime(protocol.RuntimeEvent(
|
|
149
|
+
writePreparation: protocol.WritePreparation(
|
|
150
|
+
requestId: request.requestId,
|
|
151
|
+
workspace: session,
|
|
152
|
+
)));
|
|
153
|
+
await holdOffer!.future;
|
|
154
|
+
}
|
|
145
155
|
if (refuseKernelSignerOffers > 0) {
|
|
146
156
|
refuseKernelSignerOffers -= 1;
|
|
147
157
|
return protocol.CallResponse(
|
|
@@ -225,6 +235,42 @@ Future<DartReception> _connected(_RepairTransport transport) async {
|
|
|
225
235
|
}
|
|
226
236
|
|
|
227
237
|
void main() {
|
|
238
|
+
test('runtime preparation reaches only its pending generated action observer',
|
|
239
|
+
() async {
|
|
240
|
+
final transport = _RepairTransport()..holdOffer = Completer<void>();
|
|
241
|
+
final bridge = await _connected(transport);
|
|
242
|
+
addTearDown(bridge.dispose);
|
|
243
|
+
final phases = <NomosWritePhase>[];
|
|
244
|
+
final pending = NomosWrite.observe(
|
|
245
|
+
() => bridge
|
|
246
|
+
.session('estate-1')
|
|
247
|
+
.offerGeneratedCallOutcome(call: _estateCall()),
|
|
248
|
+
onProgress: (progress) => phases.add(progress.phase));
|
|
249
|
+
await Future<void>.delayed(Duration.zero);
|
|
250
|
+
expect(phases, [NomosWritePhase.running, NomosWritePhase.preparing]);
|
|
251
|
+
transport.pushRuntime(protocol.RuntimeEvent(
|
|
252
|
+
writePreparation: protocol.WritePreparation(
|
|
253
|
+
requestId: 'another-action',
|
|
254
|
+
workspace: 'estate-1',
|
|
255
|
+
)));
|
|
256
|
+
await Future<void>.delayed(Duration.zero);
|
|
257
|
+
expect(phases.length, 2);
|
|
258
|
+
final requestId = transport.lastCall!.requestId;
|
|
259
|
+
transport.holdOffer!.complete();
|
|
260
|
+
await pending;
|
|
261
|
+
expect(phases.last, NomosWritePhase.completed);
|
|
262
|
+
expect(transport.offerCalls, 1);
|
|
263
|
+
final completed = List<NomosWritePhase>.of(phases);
|
|
264
|
+
transport.pushRuntime(protocol.RuntimeEvent(
|
|
265
|
+
writePreparation: protocol.WritePreparation(
|
|
266
|
+
requestId: requestId,
|
|
267
|
+
workspace: 'estate-1',
|
|
268
|
+
)));
|
|
269
|
+
await Future<void>.delayed(Duration.zero);
|
|
270
|
+
expect(phases, completed,
|
|
271
|
+
reason: 'completed operations detach their observers');
|
|
272
|
+
});
|
|
273
|
+
|
|
228
274
|
test('kernel unenrolled signer refusal repairs the child and retries once',
|
|
229
275
|
() async {
|
|
230
276
|
final transport = _RepairTransport()..refuseKernelSignerOffers = 1;
|
|
@@ -30,6 +30,35 @@ final class _ScriptedKernelCalls extends _ScriptedRuntime
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
void main() {
|
|
33
|
+
test('a refused generated write preserves typed sign-in and access outcomes',
|
|
34
|
+
() async {
|
|
35
|
+
for (final reason in [
|
|
36
|
+
protocol.RuntimeRefusalReason.RUNTIME_REFUSAL_REASON_INVALID_CREDENTIAL,
|
|
37
|
+
protocol.RuntimeRefusalReason.RUNTIME_REFUSAL_REASON_ACCESS_DENIED,
|
|
38
|
+
]) {
|
|
39
|
+
final runtime =
|
|
40
|
+
_ScriptedKernelCalls((request) => protocol.RuntimeResponse(
|
|
41
|
+
protocolVersion: 2,
|
|
42
|
+
requestId: request.requestId,
|
|
43
|
+
refused: protocol.RuntimeRefusal(
|
|
44
|
+
reason: reason, workspace: 'estate', retryable: false),
|
|
45
|
+
));
|
|
46
|
+
await expectLater(
|
|
47
|
+
runtime.callKernel(
|
|
48
|
+
session: 'estate',
|
|
49
|
+
request: protocol.CallRequest(),
|
|
50
|
+
credentialUse: protocol.CredentialUse.CREDENTIAL_USE_SESSION),
|
|
51
|
+
throwsA(isA<NomosRefused>().having(
|
|
52
|
+
(e) => e.reason,
|
|
53
|
+
'reason',
|
|
54
|
+
reason ==
|
|
55
|
+
protocol.RuntimeRefusalReason
|
|
56
|
+
.RUNTIME_REFUSAL_REASON_INVALID_CREDENTIAL
|
|
57
|
+
? NomosRefusalReason.invalidCredential
|
|
58
|
+
: NomosRefusalReason.accessDenied)));
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
|
|
33
62
|
test('a kernel call on a copy not ready yet is the typed wait reads sit out',
|
|
34
63
|
() async {
|
|
35
64
|
// A fresh device's first read of a workspace it is still fetching (node, web and in-app
|