@githolon/dsl 0.101.6 → 0.102.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 +13 -0
- package/dart/lib/nomos_client.dart +3 -52
- package/dart/lib/src/dev_cloud.dart +5 -0
- package/dart/lib/src/home.dart +10 -15
- package/dart/lib/src/nms_conformance_runtime.dart +12 -0
- package/dart/lib/src/nms_generated_runtime.dart +29 -0
- package/dart/lib/src/nms_test_environment.dart +8 -0
- package/dart/lib/src/node_runner_transport.dart +56 -18
- package/dart/lib/src/provider.dart +5 -1
- package/dart/lib/src/record_origin.dart +55 -0
- package/dart/lib/src/runner/node-entry.mjs +9105 -0
- package/dart/lib/src/runner/offline-install.mjs +4729 -0
- package/dart/lib/src/session_bound.dart +23 -10
- package/dart/lib/src/source_trust.dart +51 -0
- package/dart/lib/src/test_harness.dart +490 -58
- package/dart/lib/src/webview_bridge.dart +167 -58
- package/dart/lib/src/workspace_name.dart +7 -1
- package/dart/pubspec.lock +3 -3
- package/dart/pubspec.yaml +4 -2
- package/dart/test/acquire_session_credentials_test.dart +34 -17
- package/dart/test/acquire_session_test.dart +109 -24
- package/dart/test/adv_realm_interference_test.dart +227 -99
- package/dart/test/bundle_version_pairing_test.dart +1 -1
- package/dart/test/cloud_bound_topology_test.dart +1 -1
- package/dart/test/cluster_attested_test.dart +52 -35
- package/dart/test/cluster_births_test.dart +5 -2
- package/dart/test/cluster_device_keyed_acceptance_test.dart +5 -2
- package/dart/test/cluster_local_autosync_test.dart +5 -2
- package/dart/test/cluster_scale_test.dart +5 -2
- package/dart/test/compat_test.dart +1 -1
- package/dart/test/dlq_session_surface_test.dart +2 -1
- package/dart/test/errors_test.dart +1 -1
- package/dart/test/first_app_gauntlet_test.dart +78 -51
- package/dart/test/four_layer_nested_topology_test.dart +1 -1
- package/dart/test/harness_phase_timeout_test.dart +1 -1
- package/dart/test/home_parity_test.dart +9 -5
- package/dart/test/local_sync_inert_test.dart +22 -14
- package/dart/test/node_runner_transport_test.dart +9 -5
- package/dart/test/null_slot_test.dart +1 -1
- package/dart/test/parent_signer_recovery_test.dart +5 -2
- package/dart/test/schema_validation_test.dart +1 -1
- package/dart/test/sealing_client_test.dart +2 -1
- package/dart/test/session_bound_test.dart +92 -119
- package/dart/test/stale_bundle_test.dart +1 -1
- package/dart/test/test_harness_test.dart +77 -22
- package/dart/test/vo_read_typing_test.dart +32 -14
- package/dart/test/watch_after_offer_stall_test.dart +2 -1
- package/dart/test/webview_bridge_test.dart +17 -4
- package/dart/test/wire_test.dart +1 -1
- package/dart_types/CHANGELOG.md +8 -0
- package/dart_types/lib/nomos_types.dart +7 -6
- package/dart_types/lib/src/activity.dart +23 -13
- package/dart_types/lib/src/foreign_ref.dart +61 -112
- package/dart_types/lib/src/invitation.dart +96 -0
- package/dart_types/lib/src/nomos_ref.dart +97 -59
- package/dart_types/pubspec.yaml +1 -1
- package/dart_types/test/surfaced_test.dart +125 -19
- package/package.json +7 -3
- package/runtime/generated-application-host.js +8851 -0
- package/src/activity.ts +2 -2
- package/src/aggregate.ts +24 -1
- package/src/application_composition.ts +282 -0
- package/src/application_topology.ts +151 -0
- package/src/attested_core.ts +2 -2
- package/src/authoring_context.ts +9 -0
- package/src/birth_meter_ensure.ts +1 -1
- package/src/births.ts +53 -5
- package/src/build_package.ts +79 -71
- package/src/business_birth.ts +93 -10
- package/src/clear_directive.ts +3 -1
- package/src/codegen_dart.ts +369 -420
- package/src/codegen_dart_application.ts +1508 -0
- package/src/codegen_dart_client.ts +156 -95
- package/src/codegen_dart_scenario.ts +299 -0
- package/src/codegen_openapi.ts +8 -3
- package/src/codegen_proof.ts +1113 -1137
- package/src/codegen_provider_dart.ts +12 -1
- package/src/codegen_ts.ts +1176 -496
- package/src/compat_package_main.ts +22 -10
- package/src/compile_package_main.ts +1536 -337
- package/src/contract.ts +10 -4
- package/src/dart_barrel_guard.ts +5 -1
- package/src/describe_model.ts +13 -5
- package/src/directive.ts +244 -5
- package/src/engine_entry.ts +56 -13
- package/src/engine_javascript.ts +31 -0
- package/src/evolve_preview.ts +3 -12
- package/src/framework/application_home.ts +24 -10
- package/src/framework/capability.ts +15 -1
- package/src/framework/domain_lifecycle.ts +8 -0
- package/src/framework/framework_aggregates.ts +2 -2
- package/src/framework/genesis.ts +11 -1
- package/src/framework/home.ts +178 -75
- package/src/framework/impure_capability.ts +24 -7
- package/src/framework/relations.ts +13 -5
- package/src/framework/workspaces.ts +13 -12
- package/src/generated_client_names.ts +42 -3
- package/src/idof_lint.ts +65 -13
- package/src/index.ts +46 -12
- package/src/intent.ts +587 -0
- package/src/law_check.ts +64 -16
- package/src/precondition.ts +16 -4
- package/src/read_projection_namespace.ts +2 -7
- package/src/recipient.ts +33 -0
- package/src/relation_schema.ts +93 -0
- package/src/secondary_ensure.ts +11 -2
- package/src/shape_compat.ts +11 -1
- package/src/usd.ts +30 -8
- package/src/usd_layers.ts +447 -246
- package/src/usd_state.ts +1 -1
- package/src/value_object.ts +49 -13
- package/src/wire_encode.ts +124 -26
- package/src/workspace_sharing.ts +269 -0
- package/src/zod_introspection.ts +84 -0
- package/dart/example/nomos_secure_key_store.dart +0 -82
- package/dart/example/nomos_webview_transport.dart +0 -63
- package/dart/lib/nomos_headless.dart +0 -50
- package/dart/lib/testing.dart +0 -14
- package/dart/test/co2_product_gauntlet_test.dart +0 -244
- package/src/framework/identity.ts +0 -537
|
@@ -0,0 +1,4729 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __export = (target, all) => {
|
|
4
|
+
for (var name in all)
|
|
5
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
// runner/offline-install.ts
|
|
9
|
+
import { readFileSync, writeFileSync as writeFileSync2, existsSync } from "node:fs";
|
|
10
|
+
import { homedir } from "node:os";
|
|
11
|
+
import { join as join2, dirname } from "node:path";
|
|
12
|
+
import { randomBytes, webcrypto as wc } from "node:crypto";
|
|
13
|
+
import { fileURLToPath } from "node:url";
|
|
14
|
+
|
|
15
|
+
// runner/runtime-refresh.ts
|
|
16
|
+
import { statSync, renameSync, writeFileSync, mkdirSync } from "node:fs";
|
|
17
|
+
import { join } from "node:path";
|
|
18
|
+
var RUNTIME_FRESH_MS = 15 * 60 * 1e3;
|
|
19
|
+
var _refreshedDirs = /* @__PURE__ */ new Set();
|
|
20
|
+
function runtimeIsWarm(dir, freshMs = RUNTIME_FRESH_MS) {
|
|
21
|
+
try {
|
|
22
|
+
const w = statSync(join(dir, "holon.wasm")).mtimeMs;
|
|
23
|
+
const t = statSync(join(dir, "holon-testmint.wasm")).mtimeMs;
|
|
24
|
+
const p = statSync(join(dir, "packages.json")).mtimeMs;
|
|
25
|
+
return Date.now() - Math.min(w, t, p) < freshMs;
|
|
26
|
+
} catch {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function atomicWrite(target, data) {
|
|
31
|
+
const tmp = `${target}.tmp-${process.pid}-${Math.random().toString(16).slice(2)}`;
|
|
32
|
+
writeFileSync(tmp, data);
|
|
33
|
+
renameSync(tmp, target);
|
|
34
|
+
}
|
|
35
|
+
async function refreshRuntimeDir(cloud, dir, { fetchImpl = globalThis.fetch, log: log2, mode } = {}) {
|
|
36
|
+
const m = String(mode ?? process.env["NOMOS_RUNTIME_REFRESH"] ?? "auto").toLowerCase();
|
|
37
|
+
if (m === "never") return false;
|
|
38
|
+
if (m !== "always") {
|
|
39
|
+
if (_refreshedDirs.has(dir)) return false;
|
|
40
|
+
if (runtimeIsWarm(dir)) return false;
|
|
41
|
+
}
|
|
42
|
+
try {
|
|
43
|
+
const r = await fetchImpl(`${cloud}/v1/runtime/holon.wasm`);
|
|
44
|
+
if (!r.ok) throw new Error(`runtime wasm \u2192 ${r.status}`);
|
|
45
|
+
const bytes = new Uint8Array(await r.arrayBuffer());
|
|
46
|
+
const testmintResp = await fetchImpl(`${cloud}/v1/runtime/holon-testmint.wasm`);
|
|
47
|
+
if (!testmintResp.ok) throw new Error(`test-mint runtime wasm \u2192 ${testmintResp.status}`);
|
|
48
|
+
const testmintBytes = new Uint8Array(await testmintResp.arrayBuffer());
|
|
49
|
+
const pkgsResp = await fetchImpl(`${cloud}/v1/runtime/packages`);
|
|
50
|
+
if (!pkgsResp.ok) throw new Error(`runtime packages \u2192 ${pkgsResp.status}`);
|
|
51
|
+
const pkgText = await pkgsResp.text();
|
|
52
|
+
mkdirSync(dir, { recursive: true });
|
|
53
|
+
atomicWrite(join(dir, "holon.wasm"), bytes);
|
|
54
|
+
atomicWrite(join(dir, "holon-testmint.wasm"), testmintBytes);
|
|
55
|
+
atomicWrite(join(dir, "packages.json"), pkgText);
|
|
56
|
+
_refreshedDirs.add(dir);
|
|
57
|
+
if (typeof log2 === "function") log2(`offline-install: refreshed runtime cache from the cloud \u2192 ${dir}`);
|
|
58
|
+
return true;
|
|
59
|
+
} catch {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// src/_adapter/engine.ts
|
|
65
|
+
var engine_exports = {};
|
|
66
|
+
__export(engine_exports, {
|
|
67
|
+
NOMOS_SHARD_ROWS_READ: () => NOMOS_SHARD_ROWS_READ,
|
|
68
|
+
admitAll: () => admitAll,
|
|
69
|
+
admitIntentOffers: () => admitIntentOffers,
|
|
70
|
+
applyIntentBytes: () => applyIntentBytes,
|
|
71
|
+
archiveRefusedMain: () => archiveRefusedMain,
|
|
72
|
+
attestationSign: () => attestationSign,
|
|
73
|
+
attestedRead: () => attestedRead,
|
|
74
|
+
author: () => author,
|
|
75
|
+
authorEphemeral: () => authorEphemeral,
|
|
76
|
+
authorKeygen: () => authorKeygen,
|
|
77
|
+
authorOn: () => authorOn,
|
|
78
|
+
backupHead: () => backupHead,
|
|
79
|
+
backupToCustody: () => backupToCustody,
|
|
80
|
+
boot: () => boot,
|
|
81
|
+
call: () => call,
|
|
82
|
+
certSign: () => certSign,
|
|
83
|
+
check: () => check,
|
|
84
|
+
checkpointStateHash: () => checkpointStateHash,
|
|
85
|
+
clearCheckpointRefs: () => clearCheckpointRefs,
|
|
86
|
+
collectGlobalIntents: () => collectGlobalIntents,
|
|
87
|
+
collectHomedIntents: () => collectHomedIntents,
|
|
88
|
+
commitAndPush: () => commitAndPush,
|
|
89
|
+
count: () => count,
|
|
90
|
+
countRemoteCommits: () => countRemoteCommits,
|
|
91
|
+
countTenantRows: () => countTenantRows,
|
|
92
|
+
countTenantRowsByHome: () => countTenantRowsByHome,
|
|
93
|
+
createEngine: () => createEngine,
|
|
94
|
+
cryptoKeygen: () => cryptoKeygen,
|
|
95
|
+
cryptoOwnedDeviceRequest: () => cryptoOwnedDeviceRequest,
|
|
96
|
+
cryptoOwnedPrepareApproval: () => cryptoOwnedPrepareApproval,
|
|
97
|
+
cryptoOwnedPrepareRecovery: () => cryptoOwnedPrepareRecovery,
|
|
98
|
+
cryptoOwnedScopeKey: () => cryptoOwnedScopeKey,
|
|
99
|
+
cryptoSealField: () => cryptoSealField,
|
|
100
|
+
cryptoUnwrapKey: () => cryptoUnwrapKey,
|
|
101
|
+
cryptoWrapKey: () => cryptoWrapKey,
|
|
102
|
+
currentLaw: () => currentLaw,
|
|
103
|
+
declaredCapabilities: () => declaredCapabilities,
|
|
104
|
+
deepVerifyShard: () => deepVerifyShard,
|
|
105
|
+
delegationCertSign: () => delegationCertSign,
|
|
106
|
+
deleteRefusedRef: () => deleteRefusedRef,
|
|
107
|
+
deploy: () => deploy,
|
|
108
|
+
deriveSummarySuffix: () => deriveSummarySuffix,
|
|
109
|
+
describe: () => describe,
|
|
110
|
+
directiveRoutes: () => directiveRoutes,
|
|
111
|
+
domainPackage: () => domainPackage,
|
|
112
|
+
emitOutbox: () => emitOutbox,
|
|
113
|
+
eraCertSign: () => eraCertSign,
|
|
114
|
+
evalCanonicalPred: () => evalCanonicalPred,
|
|
115
|
+
evolveDryRun: () => evolveDryRun,
|
|
116
|
+
expand: () => expand,
|
|
117
|
+
exportCheckpoint: () => exportCheckpoint,
|
|
118
|
+
extremum: () => extremum,
|
|
119
|
+
flushDeferredProjections: () => flushDeferredProjections,
|
|
120
|
+
gitdirOf: () => gitdirOf,
|
|
121
|
+
globalAggregates: () => globalAggregates,
|
|
122
|
+
historyPreflight: () => historyPreflight,
|
|
123
|
+
historyRestorePreflight: () => historyRestorePreflight,
|
|
124
|
+
importCheckpoint: () => importCheckpoint,
|
|
125
|
+
ingestPackAsCustody: () => ingestPackAsCustody,
|
|
126
|
+
installPayload: () => installPayload,
|
|
127
|
+
installedLaw: () => installedLaw,
|
|
128
|
+
isWarranted: () => isWarranted,
|
|
129
|
+
kernelStamp: () => kernelStamp,
|
|
130
|
+
kgit: () => kgit,
|
|
131
|
+
lawParity: () => lawParity,
|
|
132
|
+
ledgerStats: () => ledgerStats,
|
|
133
|
+
listRefusedRefs: () => listRefusedRefs,
|
|
134
|
+
materializeGenesis: () => materializeGenesis,
|
|
135
|
+
materializeRefused: () => materializeRefused,
|
|
136
|
+
migrateHomes: () => migrateHomes,
|
|
137
|
+
mintId: () => mintId,
|
|
138
|
+
mountEphemeralWorkspace: () => mountEphemeralWorkspace,
|
|
139
|
+
mountWorkspace: () => mountWorkspace,
|
|
140
|
+
nomosActive: () => nomosActive,
|
|
141
|
+
offerAdmit: () => offerAdmit,
|
|
142
|
+
offerIntent: () => offerIntent,
|
|
143
|
+
outboxRefExists: () => outboxRefExists,
|
|
144
|
+
packBornChildren: () => packBornChildren,
|
|
145
|
+
packDelta: () => packDelta,
|
|
146
|
+
parseAttestedReadNeed: () => parseAttestedReadNeed,
|
|
147
|
+
pendingConflictSummary: () => pendingConflictSummary,
|
|
148
|
+
pendingConflicts: () => pendingConflicts,
|
|
149
|
+
placementDirectives: () => placementDirectives,
|
|
150
|
+
projectionStats: () => projectionStats,
|
|
151
|
+
promotePreflighted: () => promotePreflighted,
|
|
152
|
+
pushCheckpoint: () => pushCheckpoint,
|
|
153
|
+
pushShape: () => pushShape,
|
|
154
|
+
pushStagedCheckpoint: () => pushStagedCheckpoint,
|
|
155
|
+
qById: () => qById,
|
|
156
|
+
query: () => query,
|
|
157
|
+
queryEphemeral: () => queryEphemeral,
|
|
158
|
+
reactorScan: () => reactorScan,
|
|
159
|
+
readCheckpointBlob: () => readCheckpointBlob,
|
|
160
|
+
readPosture: () => readPosture,
|
|
161
|
+
readShape: () => readShape,
|
|
162
|
+
rebirthCompact: () => rebirthCompact,
|
|
163
|
+
reconcileBornPacks: () => reconcileBornPacks,
|
|
164
|
+
refreshProjection: () => refreshProjection,
|
|
165
|
+
refusedSummary: () => refusedSummary,
|
|
166
|
+
replicateIntents: () => replicateIntents,
|
|
167
|
+
repoArgOf: () => repoArgOf,
|
|
168
|
+
restoreCheckpointFromCustody: () => restoreCheckpointFromCustody,
|
|
169
|
+
restorePreviousPreflighted: () => restorePreviousPreflighted,
|
|
170
|
+
scopedReads: () => scopedReads,
|
|
171
|
+
sealAttestationBatches: () => sealAttestationBatches,
|
|
172
|
+
sealHomes: () => sealHomes,
|
|
173
|
+
servePack: () => servePack,
|
|
174
|
+
setTestKRoot: () => setTestKRoot,
|
|
175
|
+
sha256hex: () => sha256hex,
|
|
176
|
+
snapshot: () => snapshot,
|
|
177
|
+
spatialBboxParams: () => spatialBboxParams,
|
|
178
|
+
spatialWithin: () => spatialWithin,
|
|
179
|
+
stageCheckpoint: () => stageCheckpoint,
|
|
180
|
+
stagePayload: () => stagePayload,
|
|
181
|
+
stats: () => stats,
|
|
182
|
+
sum: () => sum,
|
|
183
|
+
summaryCloseAgainstHeld: () => summaryCloseAgainstHeld,
|
|
184
|
+
unmount: () => unmount,
|
|
185
|
+
verifyChain: () => verifyChain,
|
|
186
|
+
verifyChainLocal: () => verifyChainLocal,
|
|
187
|
+
warmEngine: () => warmEngine,
|
|
188
|
+
writeCheckpointObjects: () => writeCheckpointObjects,
|
|
189
|
+
writeOutboxRef: () => writeOutboxRef,
|
|
190
|
+
writeRefusedRef: () => writeRefusedRef,
|
|
191
|
+
wrongHomeVerdict: () => wrongHomeVerdict
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
// ../../node_modules/@bjorn3/browser_wasi_shim/dist/wasi_defs.js
|
|
195
|
+
var CLOCKID_REALTIME = 0;
|
|
196
|
+
var CLOCKID_MONOTONIC = 1;
|
|
197
|
+
var ERRNO_SUCCESS = 0;
|
|
198
|
+
var ERRNO_BADF = 8;
|
|
199
|
+
var ERRNO_EXIST = 20;
|
|
200
|
+
var ERRNO_INVAL = 28;
|
|
201
|
+
var ERRNO_ISDIR = 31;
|
|
202
|
+
var ERRNO_NAMETOOLONG = 37;
|
|
203
|
+
var ERRNO_NOENT = 44;
|
|
204
|
+
var ERRNO_NOSYS = 52;
|
|
205
|
+
var ERRNO_NOTDIR = 54;
|
|
206
|
+
var ERRNO_NOTEMPTY = 55;
|
|
207
|
+
var ERRNO_NOTSUP = 58;
|
|
208
|
+
var ERRNO_PERM = 63;
|
|
209
|
+
var ERRNO_NOTCAPABLE = 76;
|
|
210
|
+
var RIGHTS_FD_DATASYNC = 1 << 0;
|
|
211
|
+
var RIGHTS_FD_READ = 1 << 1;
|
|
212
|
+
var RIGHTS_FD_SEEK = 1 << 2;
|
|
213
|
+
var RIGHTS_FD_FDSTAT_SET_FLAGS = 1 << 3;
|
|
214
|
+
var RIGHTS_FD_SYNC = 1 << 4;
|
|
215
|
+
var RIGHTS_FD_TELL = 1 << 5;
|
|
216
|
+
var RIGHTS_FD_WRITE = 1 << 6;
|
|
217
|
+
var RIGHTS_FD_ADVISE = 1 << 7;
|
|
218
|
+
var RIGHTS_FD_ALLOCATE = 1 << 8;
|
|
219
|
+
var RIGHTS_PATH_CREATE_DIRECTORY = 1 << 9;
|
|
220
|
+
var RIGHTS_PATH_CREATE_FILE = 1 << 10;
|
|
221
|
+
var RIGHTS_PATH_LINK_SOURCE = 1 << 11;
|
|
222
|
+
var RIGHTS_PATH_LINK_TARGET = 1 << 12;
|
|
223
|
+
var RIGHTS_PATH_OPEN = 1 << 13;
|
|
224
|
+
var RIGHTS_FD_READDIR = 1 << 14;
|
|
225
|
+
var RIGHTS_PATH_READLINK = 1 << 15;
|
|
226
|
+
var RIGHTS_PATH_RENAME_SOURCE = 1 << 16;
|
|
227
|
+
var RIGHTS_PATH_RENAME_TARGET = 1 << 17;
|
|
228
|
+
var RIGHTS_PATH_FILESTAT_GET = 1 << 18;
|
|
229
|
+
var RIGHTS_PATH_FILESTAT_SET_SIZE = 1 << 19;
|
|
230
|
+
var RIGHTS_PATH_FILESTAT_SET_TIMES = 1 << 20;
|
|
231
|
+
var RIGHTS_FD_FILESTAT_GET = 1 << 21;
|
|
232
|
+
var RIGHTS_FD_FILESTAT_SET_SIZE = 1 << 22;
|
|
233
|
+
var RIGHTS_FD_FILESTAT_SET_TIMES = 1 << 23;
|
|
234
|
+
var RIGHTS_PATH_SYMLINK = 1 << 24;
|
|
235
|
+
var RIGHTS_PATH_REMOVE_DIRECTORY = 1 << 25;
|
|
236
|
+
var RIGHTS_PATH_UNLINK_FILE = 1 << 26;
|
|
237
|
+
var RIGHTS_POLL_FD_READWRITE = 1 << 27;
|
|
238
|
+
var RIGHTS_SOCK_SHUTDOWN = 1 << 28;
|
|
239
|
+
var Iovec = class _Iovec {
|
|
240
|
+
static read_bytes(view, ptr) {
|
|
241
|
+
const iovec = new _Iovec();
|
|
242
|
+
iovec.buf = view.getUint32(ptr, true);
|
|
243
|
+
iovec.buf_len = view.getUint32(ptr + 4, true);
|
|
244
|
+
return iovec;
|
|
245
|
+
}
|
|
246
|
+
static read_bytes_array(view, ptr, len) {
|
|
247
|
+
const iovecs = [];
|
|
248
|
+
for (let i = 0; i < len; i++) {
|
|
249
|
+
iovecs.push(_Iovec.read_bytes(view, ptr + 8 * i));
|
|
250
|
+
}
|
|
251
|
+
return iovecs;
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
var Ciovec = class _Ciovec {
|
|
255
|
+
static read_bytes(view, ptr) {
|
|
256
|
+
const iovec = new _Ciovec();
|
|
257
|
+
iovec.buf = view.getUint32(ptr, true);
|
|
258
|
+
iovec.buf_len = view.getUint32(ptr + 4, true);
|
|
259
|
+
return iovec;
|
|
260
|
+
}
|
|
261
|
+
static read_bytes_array(view, ptr, len) {
|
|
262
|
+
const iovecs = [];
|
|
263
|
+
for (let i = 0; i < len; i++) {
|
|
264
|
+
iovecs.push(_Ciovec.read_bytes(view, ptr + 8 * i));
|
|
265
|
+
}
|
|
266
|
+
return iovecs;
|
|
267
|
+
}
|
|
268
|
+
};
|
|
269
|
+
var WHENCE_SET = 0;
|
|
270
|
+
var WHENCE_CUR = 1;
|
|
271
|
+
var WHENCE_END = 2;
|
|
272
|
+
var FILETYPE_CHARACTER_DEVICE = 2;
|
|
273
|
+
var FILETYPE_DIRECTORY = 3;
|
|
274
|
+
var FILETYPE_REGULAR_FILE = 4;
|
|
275
|
+
var Dirent = class {
|
|
276
|
+
head_length() {
|
|
277
|
+
return 24;
|
|
278
|
+
}
|
|
279
|
+
name_length() {
|
|
280
|
+
return this.dir_name.byteLength;
|
|
281
|
+
}
|
|
282
|
+
write_head_bytes(view, ptr) {
|
|
283
|
+
view.setBigUint64(ptr, this.d_next, true);
|
|
284
|
+
view.setBigUint64(ptr + 8, this.d_ino, true);
|
|
285
|
+
view.setUint32(ptr + 16, this.dir_name.length, true);
|
|
286
|
+
view.setUint8(ptr + 20, this.d_type);
|
|
287
|
+
}
|
|
288
|
+
write_name_bytes(view8, ptr, buf_len) {
|
|
289
|
+
view8.set(this.dir_name.slice(0, Math.min(this.dir_name.byteLength, buf_len)), ptr);
|
|
290
|
+
}
|
|
291
|
+
constructor(next_cookie, d_ino, name, type) {
|
|
292
|
+
const encoded_name = new TextEncoder().encode(name);
|
|
293
|
+
this.d_next = next_cookie;
|
|
294
|
+
this.d_ino = d_ino;
|
|
295
|
+
this.d_namlen = encoded_name.byteLength;
|
|
296
|
+
this.d_type = type;
|
|
297
|
+
this.dir_name = encoded_name;
|
|
298
|
+
}
|
|
299
|
+
};
|
|
300
|
+
var FDFLAGS_APPEND = 1 << 0;
|
|
301
|
+
var FDFLAGS_DSYNC = 1 << 1;
|
|
302
|
+
var FDFLAGS_NONBLOCK = 1 << 2;
|
|
303
|
+
var FDFLAGS_RSYNC = 1 << 3;
|
|
304
|
+
var FDFLAGS_SYNC = 1 << 4;
|
|
305
|
+
var Fdstat = class {
|
|
306
|
+
write_bytes(view, ptr) {
|
|
307
|
+
view.setUint8(ptr, this.fs_filetype);
|
|
308
|
+
view.setUint16(ptr + 2, this.fs_flags, true);
|
|
309
|
+
view.setBigUint64(ptr + 8, this.fs_rights_base, true);
|
|
310
|
+
view.setBigUint64(ptr + 16, this.fs_rights_inherited, true);
|
|
311
|
+
}
|
|
312
|
+
constructor(filetype, flags) {
|
|
313
|
+
this.fs_rights_base = 0n;
|
|
314
|
+
this.fs_rights_inherited = 0n;
|
|
315
|
+
this.fs_filetype = filetype;
|
|
316
|
+
this.fs_flags = flags;
|
|
317
|
+
}
|
|
318
|
+
};
|
|
319
|
+
var FSTFLAGS_ATIM = 1 << 0;
|
|
320
|
+
var FSTFLAGS_ATIM_NOW = 1 << 1;
|
|
321
|
+
var FSTFLAGS_MTIM = 1 << 2;
|
|
322
|
+
var FSTFLAGS_MTIM_NOW = 1 << 3;
|
|
323
|
+
var OFLAGS_CREAT = 1 << 0;
|
|
324
|
+
var OFLAGS_DIRECTORY = 1 << 1;
|
|
325
|
+
var OFLAGS_EXCL = 1 << 2;
|
|
326
|
+
var OFLAGS_TRUNC = 1 << 3;
|
|
327
|
+
var Filestat = class {
|
|
328
|
+
write_bytes(view, ptr) {
|
|
329
|
+
view.setBigUint64(ptr, this.dev, true);
|
|
330
|
+
view.setBigUint64(ptr + 8, this.ino, true);
|
|
331
|
+
view.setUint8(ptr + 16, this.filetype);
|
|
332
|
+
view.setBigUint64(ptr + 24, this.nlink, true);
|
|
333
|
+
view.setBigUint64(ptr + 32, this.size, true);
|
|
334
|
+
view.setBigUint64(ptr + 38, this.atim, true);
|
|
335
|
+
view.setBigUint64(ptr + 46, this.mtim, true);
|
|
336
|
+
view.setBigUint64(ptr + 52, this.ctim, true);
|
|
337
|
+
}
|
|
338
|
+
constructor(ino, filetype, size) {
|
|
339
|
+
this.dev = 0n;
|
|
340
|
+
this.nlink = 0n;
|
|
341
|
+
this.atim = 0n;
|
|
342
|
+
this.mtim = 0n;
|
|
343
|
+
this.ctim = 0n;
|
|
344
|
+
this.ino = ino;
|
|
345
|
+
this.filetype = filetype;
|
|
346
|
+
this.size = size;
|
|
347
|
+
}
|
|
348
|
+
};
|
|
349
|
+
var EVENTTYPE_CLOCK = 0;
|
|
350
|
+
var EVENTRWFLAGS_FD_READWRITE_HANGUP = 1 << 0;
|
|
351
|
+
var SUBCLOCKFLAGS_SUBSCRIPTION_CLOCK_ABSTIME = 1 << 0;
|
|
352
|
+
var Subscription = class _Subscription {
|
|
353
|
+
static read_bytes(view, ptr) {
|
|
354
|
+
return new _Subscription(view.getBigUint64(ptr, true), view.getUint8(ptr + 8), view.getUint32(ptr + 16, true), view.getBigUint64(ptr + 24, true), view.getUint16(ptr + 36, true));
|
|
355
|
+
}
|
|
356
|
+
constructor(userdata, eventtype, clockid, timeout, flags) {
|
|
357
|
+
this.userdata = userdata;
|
|
358
|
+
this.eventtype = eventtype;
|
|
359
|
+
this.clockid = clockid;
|
|
360
|
+
this.timeout = timeout;
|
|
361
|
+
this.flags = flags;
|
|
362
|
+
}
|
|
363
|
+
};
|
|
364
|
+
var Event = class {
|
|
365
|
+
write_bytes(view, ptr) {
|
|
366
|
+
view.setBigUint64(ptr, this.userdata, true);
|
|
367
|
+
view.setUint16(ptr + 8, this.error, true);
|
|
368
|
+
view.setUint8(ptr + 10, this.eventtype);
|
|
369
|
+
}
|
|
370
|
+
constructor(userdata, error, eventtype) {
|
|
371
|
+
this.userdata = userdata;
|
|
372
|
+
this.error = error;
|
|
373
|
+
this.eventtype = eventtype;
|
|
374
|
+
}
|
|
375
|
+
};
|
|
376
|
+
var RIFLAGS_RECV_PEEK = 1 << 0;
|
|
377
|
+
var RIFLAGS_RECV_WAITALL = 1 << 1;
|
|
378
|
+
var ROFLAGS_RECV_DATA_TRUNCATED = 1 << 0;
|
|
379
|
+
var SDFLAGS_RD = 1 << 0;
|
|
380
|
+
var SDFLAGS_WR = 1 << 1;
|
|
381
|
+
var PREOPENTYPE_DIR = 0;
|
|
382
|
+
var PrestatDir = class {
|
|
383
|
+
write_bytes(view, ptr) {
|
|
384
|
+
view.setUint32(ptr, this.pr_name.byteLength, true);
|
|
385
|
+
}
|
|
386
|
+
constructor(name) {
|
|
387
|
+
this.pr_name = new TextEncoder().encode(name);
|
|
388
|
+
}
|
|
389
|
+
};
|
|
390
|
+
var Prestat = class _Prestat {
|
|
391
|
+
static dir(name) {
|
|
392
|
+
const prestat = new _Prestat();
|
|
393
|
+
prestat.tag = PREOPENTYPE_DIR;
|
|
394
|
+
prestat.inner = new PrestatDir(name);
|
|
395
|
+
return prestat;
|
|
396
|
+
}
|
|
397
|
+
write_bytes(view, ptr) {
|
|
398
|
+
view.setUint32(ptr, this.tag, true);
|
|
399
|
+
this.inner.write_bytes(view, ptr + 4);
|
|
400
|
+
}
|
|
401
|
+
};
|
|
402
|
+
|
|
403
|
+
// ../../node_modules/@bjorn3/browser_wasi_shim/dist/debug.js
|
|
404
|
+
var Debug = class Debug2 {
|
|
405
|
+
enable(enabled) {
|
|
406
|
+
this.log = createLogger(enabled === void 0 ? true : enabled, this.prefix);
|
|
407
|
+
}
|
|
408
|
+
get enabled() {
|
|
409
|
+
return this.isEnabled;
|
|
410
|
+
}
|
|
411
|
+
constructor(isEnabled) {
|
|
412
|
+
this.isEnabled = isEnabled;
|
|
413
|
+
this.prefix = "wasi:";
|
|
414
|
+
this.enable(isEnabled);
|
|
415
|
+
}
|
|
416
|
+
};
|
|
417
|
+
function createLogger(enabled, prefix) {
|
|
418
|
+
if (enabled) {
|
|
419
|
+
const a = console.log.bind(console, "%c%s", "color: #265BA0", prefix);
|
|
420
|
+
return a;
|
|
421
|
+
} else {
|
|
422
|
+
return () => {
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
var debug = new Debug(false);
|
|
427
|
+
|
|
428
|
+
// ../../node_modules/@bjorn3/browser_wasi_shim/dist/wasi.js
|
|
429
|
+
var WASIProcExit = class extends Error {
|
|
430
|
+
constructor(code) {
|
|
431
|
+
super("exit with exit code " + code);
|
|
432
|
+
this.code = code;
|
|
433
|
+
}
|
|
434
|
+
};
|
|
435
|
+
var WASI = class WASI2 {
|
|
436
|
+
start(instance) {
|
|
437
|
+
this.inst = instance;
|
|
438
|
+
try {
|
|
439
|
+
instance.exports._start();
|
|
440
|
+
return 0;
|
|
441
|
+
} catch (e) {
|
|
442
|
+
if (e instanceof WASIProcExit) {
|
|
443
|
+
return e.code;
|
|
444
|
+
} else {
|
|
445
|
+
throw e;
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
initialize(instance) {
|
|
450
|
+
this.inst = instance;
|
|
451
|
+
if (instance.exports._initialize) {
|
|
452
|
+
instance.exports._initialize();
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
constructor(args, env, fds, options = {}) {
|
|
456
|
+
this.args = [];
|
|
457
|
+
this.env = [];
|
|
458
|
+
this.fds = [];
|
|
459
|
+
debug.enable(options.debug);
|
|
460
|
+
this.args = args;
|
|
461
|
+
this.env = env;
|
|
462
|
+
this.fds = fds;
|
|
463
|
+
const self = this;
|
|
464
|
+
this.wasiImport = { args_sizes_get(argc, argv_buf_size) {
|
|
465
|
+
const buffer = new DataView(self.inst.exports.memory.buffer);
|
|
466
|
+
buffer.setUint32(argc, self.args.length, true);
|
|
467
|
+
let buf_size = 0;
|
|
468
|
+
for (const arg2 of self.args) {
|
|
469
|
+
buf_size += arg2.length + 1;
|
|
470
|
+
}
|
|
471
|
+
buffer.setUint32(argv_buf_size, buf_size, true);
|
|
472
|
+
debug.log(buffer.getUint32(argc, true), buffer.getUint32(argv_buf_size, true));
|
|
473
|
+
return 0;
|
|
474
|
+
}, args_get(argv, argv_buf) {
|
|
475
|
+
const buffer = new DataView(self.inst.exports.memory.buffer);
|
|
476
|
+
const buffer8 = new Uint8Array(self.inst.exports.memory.buffer);
|
|
477
|
+
const orig_argv_buf = argv_buf;
|
|
478
|
+
for (let i = 0; i < self.args.length; i++) {
|
|
479
|
+
buffer.setUint32(argv, argv_buf, true);
|
|
480
|
+
argv += 4;
|
|
481
|
+
const arg2 = new TextEncoder().encode(self.args[i]);
|
|
482
|
+
buffer8.set(arg2, argv_buf);
|
|
483
|
+
buffer.setUint8(argv_buf + arg2.length, 0);
|
|
484
|
+
argv_buf += arg2.length + 1;
|
|
485
|
+
}
|
|
486
|
+
if (debug.enabled) {
|
|
487
|
+
debug.log(new TextDecoder("utf-8").decode(buffer8.slice(orig_argv_buf, argv_buf)));
|
|
488
|
+
}
|
|
489
|
+
return 0;
|
|
490
|
+
}, environ_sizes_get(environ_count, environ_size) {
|
|
491
|
+
const buffer = new DataView(self.inst.exports.memory.buffer);
|
|
492
|
+
buffer.setUint32(environ_count, self.env.length, true);
|
|
493
|
+
let buf_size = 0;
|
|
494
|
+
for (const environ of self.env) {
|
|
495
|
+
buf_size += new TextEncoder().encode(environ).length + 1;
|
|
496
|
+
}
|
|
497
|
+
buffer.setUint32(environ_size, buf_size, true);
|
|
498
|
+
debug.log(buffer.getUint32(environ_count, true), buffer.getUint32(environ_size, true));
|
|
499
|
+
return 0;
|
|
500
|
+
}, environ_get(environ, environ_buf) {
|
|
501
|
+
const buffer = new DataView(self.inst.exports.memory.buffer);
|
|
502
|
+
const buffer8 = new Uint8Array(self.inst.exports.memory.buffer);
|
|
503
|
+
const orig_environ_buf = environ_buf;
|
|
504
|
+
for (let i = 0; i < self.env.length; i++) {
|
|
505
|
+
buffer.setUint32(environ, environ_buf, true);
|
|
506
|
+
environ += 4;
|
|
507
|
+
const e = new TextEncoder().encode(self.env[i]);
|
|
508
|
+
buffer8.set(e, environ_buf);
|
|
509
|
+
buffer.setUint8(environ_buf + e.length, 0);
|
|
510
|
+
environ_buf += e.length + 1;
|
|
511
|
+
}
|
|
512
|
+
if (debug.enabled) {
|
|
513
|
+
debug.log(new TextDecoder("utf-8").decode(buffer8.slice(orig_environ_buf, environ_buf)));
|
|
514
|
+
}
|
|
515
|
+
return 0;
|
|
516
|
+
}, clock_res_get(id, res_ptr) {
|
|
517
|
+
let resolutionValue;
|
|
518
|
+
switch (id) {
|
|
519
|
+
case CLOCKID_MONOTONIC: {
|
|
520
|
+
resolutionValue = 5000n;
|
|
521
|
+
break;
|
|
522
|
+
}
|
|
523
|
+
case CLOCKID_REALTIME: {
|
|
524
|
+
resolutionValue = 1000000n;
|
|
525
|
+
break;
|
|
526
|
+
}
|
|
527
|
+
default:
|
|
528
|
+
return ERRNO_NOSYS;
|
|
529
|
+
}
|
|
530
|
+
const view = new DataView(self.inst.exports.memory.buffer);
|
|
531
|
+
view.setBigUint64(res_ptr, resolutionValue, true);
|
|
532
|
+
return ERRNO_SUCCESS;
|
|
533
|
+
}, clock_time_get(id, precision, time) {
|
|
534
|
+
const buffer = new DataView(self.inst.exports.memory.buffer);
|
|
535
|
+
if (id === CLOCKID_REALTIME) {
|
|
536
|
+
buffer.setBigUint64(time, BigInt((/* @__PURE__ */ new Date()).getTime()) * 1000000n, true);
|
|
537
|
+
} else if (id == CLOCKID_MONOTONIC) {
|
|
538
|
+
let monotonic_time;
|
|
539
|
+
try {
|
|
540
|
+
monotonic_time = BigInt(Math.round(performance.now() * 1e6));
|
|
541
|
+
} catch (e) {
|
|
542
|
+
monotonic_time = 0n;
|
|
543
|
+
}
|
|
544
|
+
buffer.setBigUint64(time, monotonic_time, true);
|
|
545
|
+
} else {
|
|
546
|
+
buffer.setBigUint64(time, 0n, true);
|
|
547
|
+
}
|
|
548
|
+
return 0;
|
|
549
|
+
}, fd_advise(fd, offset, len, advice) {
|
|
550
|
+
if (self.fds[fd] != void 0) {
|
|
551
|
+
return ERRNO_SUCCESS;
|
|
552
|
+
} else {
|
|
553
|
+
return ERRNO_BADF;
|
|
554
|
+
}
|
|
555
|
+
}, fd_allocate(fd, offset, len) {
|
|
556
|
+
if (self.fds[fd] != void 0) {
|
|
557
|
+
return self.fds[fd].fd_allocate(offset, len);
|
|
558
|
+
} else {
|
|
559
|
+
return ERRNO_BADF;
|
|
560
|
+
}
|
|
561
|
+
}, fd_close(fd) {
|
|
562
|
+
if (self.fds[fd] != void 0) {
|
|
563
|
+
const ret = self.fds[fd].fd_close();
|
|
564
|
+
self.fds[fd] = void 0;
|
|
565
|
+
return ret;
|
|
566
|
+
} else {
|
|
567
|
+
return ERRNO_BADF;
|
|
568
|
+
}
|
|
569
|
+
}, fd_datasync(fd) {
|
|
570
|
+
if (self.fds[fd] != void 0) {
|
|
571
|
+
return self.fds[fd].fd_sync();
|
|
572
|
+
} else {
|
|
573
|
+
return ERRNO_BADF;
|
|
574
|
+
}
|
|
575
|
+
}, fd_fdstat_get(fd, fdstat_ptr) {
|
|
576
|
+
if (self.fds[fd] != void 0) {
|
|
577
|
+
const { ret, fdstat } = self.fds[fd].fd_fdstat_get();
|
|
578
|
+
if (fdstat != null) {
|
|
579
|
+
fdstat.write_bytes(new DataView(self.inst.exports.memory.buffer), fdstat_ptr);
|
|
580
|
+
}
|
|
581
|
+
return ret;
|
|
582
|
+
} else {
|
|
583
|
+
return ERRNO_BADF;
|
|
584
|
+
}
|
|
585
|
+
}, fd_fdstat_set_flags(fd, flags) {
|
|
586
|
+
if (self.fds[fd] != void 0) {
|
|
587
|
+
return self.fds[fd].fd_fdstat_set_flags(flags);
|
|
588
|
+
} else {
|
|
589
|
+
return ERRNO_BADF;
|
|
590
|
+
}
|
|
591
|
+
}, fd_fdstat_set_rights(fd, fs_rights_base, fs_rights_inheriting) {
|
|
592
|
+
if (self.fds[fd] != void 0) {
|
|
593
|
+
return self.fds[fd].fd_fdstat_set_rights(fs_rights_base, fs_rights_inheriting);
|
|
594
|
+
} else {
|
|
595
|
+
return ERRNO_BADF;
|
|
596
|
+
}
|
|
597
|
+
}, fd_filestat_get(fd, filestat_ptr) {
|
|
598
|
+
if (self.fds[fd] != void 0) {
|
|
599
|
+
const { ret, filestat } = self.fds[fd].fd_filestat_get();
|
|
600
|
+
if (filestat != null) {
|
|
601
|
+
filestat.write_bytes(new DataView(self.inst.exports.memory.buffer), filestat_ptr);
|
|
602
|
+
}
|
|
603
|
+
return ret;
|
|
604
|
+
} else {
|
|
605
|
+
return ERRNO_BADF;
|
|
606
|
+
}
|
|
607
|
+
}, fd_filestat_set_size(fd, size) {
|
|
608
|
+
if (self.fds[fd] != void 0) {
|
|
609
|
+
return self.fds[fd].fd_filestat_set_size(size);
|
|
610
|
+
} else {
|
|
611
|
+
return ERRNO_BADF;
|
|
612
|
+
}
|
|
613
|
+
}, fd_filestat_set_times(fd, atim, mtim, fst_flags) {
|
|
614
|
+
if (self.fds[fd] != void 0) {
|
|
615
|
+
return self.fds[fd].fd_filestat_set_times(atim, mtim, fst_flags);
|
|
616
|
+
} else {
|
|
617
|
+
return ERRNO_BADF;
|
|
618
|
+
}
|
|
619
|
+
}, fd_pread(fd, iovs_ptr, iovs_len, offset, nread_ptr) {
|
|
620
|
+
const buffer = new DataView(self.inst.exports.memory.buffer);
|
|
621
|
+
const buffer8 = new Uint8Array(self.inst.exports.memory.buffer);
|
|
622
|
+
if (self.fds[fd] != void 0) {
|
|
623
|
+
const iovecs = Iovec.read_bytes_array(buffer, iovs_ptr, iovs_len);
|
|
624
|
+
let nread = 0;
|
|
625
|
+
for (const iovec of iovecs) {
|
|
626
|
+
const { ret, data } = self.fds[fd].fd_pread(iovec.buf_len, offset);
|
|
627
|
+
if (ret != ERRNO_SUCCESS) {
|
|
628
|
+
buffer.setUint32(nread_ptr, nread, true);
|
|
629
|
+
return ret;
|
|
630
|
+
}
|
|
631
|
+
buffer8.set(data, iovec.buf);
|
|
632
|
+
nread += data.length;
|
|
633
|
+
offset += BigInt(data.length);
|
|
634
|
+
if (data.length != iovec.buf_len) {
|
|
635
|
+
break;
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
buffer.setUint32(nread_ptr, nread, true);
|
|
639
|
+
return ERRNO_SUCCESS;
|
|
640
|
+
} else {
|
|
641
|
+
return ERRNO_BADF;
|
|
642
|
+
}
|
|
643
|
+
}, fd_prestat_get(fd, buf_ptr) {
|
|
644
|
+
const buffer = new DataView(self.inst.exports.memory.buffer);
|
|
645
|
+
if (self.fds[fd] != void 0) {
|
|
646
|
+
const { ret, prestat } = self.fds[fd].fd_prestat_get();
|
|
647
|
+
if (prestat != null) {
|
|
648
|
+
prestat.write_bytes(buffer, buf_ptr);
|
|
649
|
+
}
|
|
650
|
+
return ret;
|
|
651
|
+
} else {
|
|
652
|
+
return ERRNO_BADF;
|
|
653
|
+
}
|
|
654
|
+
}, fd_prestat_dir_name(fd, path_ptr, path_len) {
|
|
655
|
+
if (self.fds[fd] != void 0) {
|
|
656
|
+
const { ret, prestat } = self.fds[fd].fd_prestat_get();
|
|
657
|
+
if (prestat == null) {
|
|
658
|
+
return ret;
|
|
659
|
+
}
|
|
660
|
+
const prestat_dir_name = prestat.inner.pr_name;
|
|
661
|
+
const buffer8 = new Uint8Array(self.inst.exports.memory.buffer);
|
|
662
|
+
buffer8.set(prestat_dir_name.slice(0, path_len), path_ptr);
|
|
663
|
+
return prestat_dir_name.byteLength > path_len ? ERRNO_NAMETOOLONG : ERRNO_SUCCESS;
|
|
664
|
+
} else {
|
|
665
|
+
return ERRNO_BADF;
|
|
666
|
+
}
|
|
667
|
+
}, fd_pwrite(fd, iovs_ptr, iovs_len, offset, nwritten_ptr) {
|
|
668
|
+
const buffer = new DataView(self.inst.exports.memory.buffer);
|
|
669
|
+
const buffer8 = new Uint8Array(self.inst.exports.memory.buffer);
|
|
670
|
+
if (self.fds[fd] != void 0) {
|
|
671
|
+
const iovecs = Ciovec.read_bytes_array(buffer, iovs_ptr, iovs_len);
|
|
672
|
+
let nwritten = 0;
|
|
673
|
+
for (const iovec of iovecs) {
|
|
674
|
+
const data = buffer8.slice(iovec.buf, iovec.buf + iovec.buf_len);
|
|
675
|
+
const { ret, nwritten: nwritten_part } = self.fds[fd].fd_pwrite(data, offset);
|
|
676
|
+
if (ret != ERRNO_SUCCESS) {
|
|
677
|
+
buffer.setUint32(nwritten_ptr, nwritten, true);
|
|
678
|
+
return ret;
|
|
679
|
+
}
|
|
680
|
+
nwritten += nwritten_part;
|
|
681
|
+
offset += BigInt(nwritten_part);
|
|
682
|
+
if (nwritten_part != data.byteLength) {
|
|
683
|
+
break;
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
buffer.setUint32(nwritten_ptr, nwritten, true);
|
|
687
|
+
return ERRNO_SUCCESS;
|
|
688
|
+
} else {
|
|
689
|
+
return ERRNO_BADF;
|
|
690
|
+
}
|
|
691
|
+
}, fd_read(fd, iovs_ptr, iovs_len, nread_ptr) {
|
|
692
|
+
const buffer = new DataView(self.inst.exports.memory.buffer);
|
|
693
|
+
const buffer8 = new Uint8Array(self.inst.exports.memory.buffer);
|
|
694
|
+
if (self.fds[fd] != void 0) {
|
|
695
|
+
const iovecs = Iovec.read_bytes_array(buffer, iovs_ptr, iovs_len);
|
|
696
|
+
let nread = 0;
|
|
697
|
+
for (const iovec of iovecs) {
|
|
698
|
+
const { ret, data } = self.fds[fd].fd_read(iovec.buf_len);
|
|
699
|
+
if (ret != ERRNO_SUCCESS) {
|
|
700
|
+
buffer.setUint32(nread_ptr, nread, true);
|
|
701
|
+
return ret;
|
|
702
|
+
}
|
|
703
|
+
buffer8.set(data, iovec.buf);
|
|
704
|
+
nread += data.length;
|
|
705
|
+
if (data.length != iovec.buf_len) {
|
|
706
|
+
break;
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
buffer.setUint32(nread_ptr, nread, true);
|
|
710
|
+
return ERRNO_SUCCESS;
|
|
711
|
+
} else {
|
|
712
|
+
return ERRNO_BADF;
|
|
713
|
+
}
|
|
714
|
+
}, fd_readdir(fd, buf, buf_len, cookie, bufused_ptr) {
|
|
715
|
+
const buffer = new DataView(self.inst.exports.memory.buffer);
|
|
716
|
+
const buffer8 = new Uint8Array(self.inst.exports.memory.buffer);
|
|
717
|
+
if (self.fds[fd] != void 0) {
|
|
718
|
+
let bufused = 0;
|
|
719
|
+
while (true) {
|
|
720
|
+
const { ret, dirent } = self.fds[fd].fd_readdir_single(cookie);
|
|
721
|
+
if (ret != 0) {
|
|
722
|
+
buffer.setUint32(bufused_ptr, bufused, true);
|
|
723
|
+
return ret;
|
|
724
|
+
}
|
|
725
|
+
if (dirent == null) {
|
|
726
|
+
break;
|
|
727
|
+
}
|
|
728
|
+
if (buf_len - bufused < dirent.head_length()) {
|
|
729
|
+
bufused = buf_len;
|
|
730
|
+
break;
|
|
731
|
+
}
|
|
732
|
+
const head_bytes = new ArrayBuffer(dirent.head_length());
|
|
733
|
+
dirent.write_head_bytes(new DataView(head_bytes), 0);
|
|
734
|
+
buffer8.set(new Uint8Array(head_bytes).slice(0, Math.min(head_bytes.byteLength, buf_len - bufused)), buf);
|
|
735
|
+
buf += dirent.head_length();
|
|
736
|
+
bufused += dirent.head_length();
|
|
737
|
+
if (buf_len - bufused < dirent.name_length()) {
|
|
738
|
+
bufused = buf_len;
|
|
739
|
+
break;
|
|
740
|
+
}
|
|
741
|
+
dirent.write_name_bytes(buffer8, buf, buf_len - bufused);
|
|
742
|
+
buf += dirent.name_length();
|
|
743
|
+
bufused += dirent.name_length();
|
|
744
|
+
cookie = dirent.d_next;
|
|
745
|
+
}
|
|
746
|
+
buffer.setUint32(bufused_ptr, bufused, true);
|
|
747
|
+
return 0;
|
|
748
|
+
} else {
|
|
749
|
+
return ERRNO_BADF;
|
|
750
|
+
}
|
|
751
|
+
}, fd_renumber(fd, to) {
|
|
752
|
+
if (self.fds[fd] != void 0 && self.fds[to] != void 0) {
|
|
753
|
+
const ret = self.fds[to].fd_close();
|
|
754
|
+
if (ret != 0) {
|
|
755
|
+
return ret;
|
|
756
|
+
}
|
|
757
|
+
self.fds[to] = self.fds[fd];
|
|
758
|
+
self.fds[fd] = void 0;
|
|
759
|
+
return 0;
|
|
760
|
+
} else {
|
|
761
|
+
return ERRNO_BADF;
|
|
762
|
+
}
|
|
763
|
+
}, fd_seek(fd, offset, whence, offset_out_ptr) {
|
|
764
|
+
const buffer = new DataView(self.inst.exports.memory.buffer);
|
|
765
|
+
if (self.fds[fd] != void 0) {
|
|
766
|
+
const { ret, offset: offset_out } = self.fds[fd].fd_seek(offset, whence);
|
|
767
|
+
buffer.setBigInt64(offset_out_ptr, offset_out, true);
|
|
768
|
+
return ret;
|
|
769
|
+
} else {
|
|
770
|
+
return ERRNO_BADF;
|
|
771
|
+
}
|
|
772
|
+
}, fd_sync(fd) {
|
|
773
|
+
if (self.fds[fd] != void 0) {
|
|
774
|
+
return self.fds[fd].fd_sync();
|
|
775
|
+
} else {
|
|
776
|
+
return ERRNO_BADF;
|
|
777
|
+
}
|
|
778
|
+
}, fd_tell(fd, offset_ptr) {
|
|
779
|
+
const buffer = new DataView(self.inst.exports.memory.buffer);
|
|
780
|
+
if (self.fds[fd] != void 0) {
|
|
781
|
+
const { ret, offset } = self.fds[fd].fd_tell();
|
|
782
|
+
buffer.setBigUint64(offset_ptr, offset, true);
|
|
783
|
+
return ret;
|
|
784
|
+
} else {
|
|
785
|
+
return ERRNO_BADF;
|
|
786
|
+
}
|
|
787
|
+
}, fd_write(fd, iovs_ptr, iovs_len, nwritten_ptr) {
|
|
788
|
+
const buffer = new DataView(self.inst.exports.memory.buffer);
|
|
789
|
+
const buffer8 = new Uint8Array(self.inst.exports.memory.buffer);
|
|
790
|
+
if (self.fds[fd] != void 0) {
|
|
791
|
+
const iovecs = Ciovec.read_bytes_array(buffer, iovs_ptr, iovs_len);
|
|
792
|
+
let nwritten = 0;
|
|
793
|
+
for (const iovec of iovecs) {
|
|
794
|
+
const data = buffer8.slice(iovec.buf, iovec.buf + iovec.buf_len);
|
|
795
|
+
const { ret, nwritten: nwritten_part } = self.fds[fd].fd_write(data);
|
|
796
|
+
if (ret != ERRNO_SUCCESS) {
|
|
797
|
+
buffer.setUint32(nwritten_ptr, nwritten, true);
|
|
798
|
+
return ret;
|
|
799
|
+
}
|
|
800
|
+
nwritten += nwritten_part;
|
|
801
|
+
if (nwritten_part != data.byteLength) {
|
|
802
|
+
break;
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
buffer.setUint32(nwritten_ptr, nwritten, true);
|
|
806
|
+
return ERRNO_SUCCESS;
|
|
807
|
+
} else {
|
|
808
|
+
return ERRNO_BADF;
|
|
809
|
+
}
|
|
810
|
+
}, path_create_directory(fd, path_ptr, path_len) {
|
|
811
|
+
const buffer8 = new Uint8Array(self.inst.exports.memory.buffer);
|
|
812
|
+
if (self.fds[fd] != void 0) {
|
|
813
|
+
const path = new TextDecoder("utf-8").decode(buffer8.slice(path_ptr, path_ptr + path_len));
|
|
814
|
+
return self.fds[fd].path_create_directory(path);
|
|
815
|
+
} else {
|
|
816
|
+
return ERRNO_BADF;
|
|
817
|
+
}
|
|
818
|
+
}, path_filestat_get(fd, flags, path_ptr, path_len, filestat_ptr) {
|
|
819
|
+
const buffer = new DataView(self.inst.exports.memory.buffer);
|
|
820
|
+
const buffer8 = new Uint8Array(self.inst.exports.memory.buffer);
|
|
821
|
+
if (self.fds[fd] != void 0) {
|
|
822
|
+
const path = new TextDecoder("utf-8").decode(buffer8.slice(path_ptr, path_ptr + path_len));
|
|
823
|
+
const { ret, filestat } = self.fds[fd].path_filestat_get(flags, path);
|
|
824
|
+
if (filestat != null) {
|
|
825
|
+
filestat.write_bytes(buffer, filestat_ptr);
|
|
826
|
+
}
|
|
827
|
+
return ret;
|
|
828
|
+
} else {
|
|
829
|
+
return ERRNO_BADF;
|
|
830
|
+
}
|
|
831
|
+
}, path_filestat_set_times(fd, flags, path_ptr, path_len, atim, mtim, fst_flags) {
|
|
832
|
+
const buffer8 = new Uint8Array(self.inst.exports.memory.buffer);
|
|
833
|
+
if (self.fds[fd] != void 0) {
|
|
834
|
+
const path = new TextDecoder("utf-8").decode(buffer8.slice(path_ptr, path_ptr + path_len));
|
|
835
|
+
return self.fds[fd].path_filestat_set_times(flags, path, atim, mtim, fst_flags);
|
|
836
|
+
} else {
|
|
837
|
+
return ERRNO_BADF;
|
|
838
|
+
}
|
|
839
|
+
}, path_link(old_fd, old_flags, old_path_ptr, old_path_len, new_fd, new_path_ptr, new_path_len) {
|
|
840
|
+
const buffer8 = new Uint8Array(self.inst.exports.memory.buffer);
|
|
841
|
+
if (self.fds[old_fd] != void 0 && self.fds[new_fd] != void 0) {
|
|
842
|
+
const old_path = new TextDecoder("utf-8").decode(buffer8.slice(old_path_ptr, old_path_ptr + old_path_len));
|
|
843
|
+
const new_path = new TextDecoder("utf-8").decode(buffer8.slice(new_path_ptr, new_path_ptr + new_path_len));
|
|
844
|
+
const { ret, inode_obj } = self.fds[old_fd].path_lookup(old_path, old_flags);
|
|
845
|
+
if (inode_obj == null) {
|
|
846
|
+
return ret;
|
|
847
|
+
}
|
|
848
|
+
return self.fds[new_fd].path_link(new_path, inode_obj, false);
|
|
849
|
+
} else {
|
|
850
|
+
return ERRNO_BADF;
|
|
851
|
+
}
|
|
852
|
+
}, path_open(fd, dirflags, path_ptr, path_len, oflags, fs_rights_base, fs_rights_inheriting, fd_flags, opened_fd_ptr) {
|
|
853
|
+
const buffer = new DataView(self.inst.exports.memory.buffer);
|
|
854
|
+
const buffer8 = new Uint8Array(self.inst.exports.memory.buffer);
|
|
855
|
+
if (self.fds[fd] != void 0) {
|
|
856
|
+
const path = new TextDecoder("utf-8").decode(buffer8.slice(path_ptr, path_ptr + path_len));
|
|
857
|
+
debug.log(path);
|
|
858
|
+
const { ret, fd_obj } = self.fds[fd].path_open(dirflags, path, oflags, fs_rights_base, fs_rights_inheriting, fd_flags);
|
|
859
|
+
if (ret != 0) {
|
|
860
|
+
return ret;
|
|
861
|
+
}
|
|
862
|
+
self.fds.push(fd_obj);
|
|
863
|
+
const opened_fd = self.fds.length - 1;
|
|
864
|
+
buffer.setUint32(opened_fd_ptr, opened_fd, true);
|
|
865
|
+
return 0;
|
|
866
|
+
} else {
|
|
867
|
+
return ERRNO_BADF;
|
|
868
|
+
}
|
|
869
|
+
}, path_readlink(fd, path_ptr, path_len, buf_ptr, buf_len, nread_ptr) {
|
|
870
|
+
const buffer = new DataView(self.inst.exports.memory.buffer);
|
|
871
|
+
const buffer8 = new Uint8Array(self.inst.exports.memory.buffer);
|
|
872
|
+
if (self.fds[fd] != void 0) {
|
|
873
|
+
const path = new TextDecoder("utf-8").decode(buffer8.slice(path_ptr, path_ptr + path_len));
|
|
874
|
+
debug.log(path);
|
|
875
|
+
const { ret, data } = self.fds[fd].path_readlink(path);
|
|
876
|
+
if (data != null) {
|
|
877
|
+
const data_buf = new TextEncoder().encode(data);
|
|
878
|
+
if (data_buf.length > buf_len) {
|
|
879
|
+
buffer.setUint32(nread_ptr, 0, true);
|
|
880
|
+
return ERRNO_BADF;
|
|
881
|
+
}
|
|
882
|
+
buffer8.set(data_buf, buf_ptr);
|
|
883
|
+
buffer.setUint32(nread_ptr, data_buf.length, true);
|
|
884
|
+
}
|
|
885
|
+
return ret;
|
|
886
|
+
} else {
|
|
887
|
+
return ERRNO_BADF;
|
|
888
|
+
}
|
|
889
|
+
}, path_remove_directory(fd, path_ptr, path_len) {
|
|
890
|
+
const buffer8 = new Uint8Array(self.inst.exports.memory.buffer);
|
|
891
|
+
if (self.fds[fd] != void 0) {
|
|
892
|
+
const path = new TextDecoder("utf-8").decode(buffer8.slice(path_ptr, path_ptr + path_len));
|
|
893
|
+
return self.fds[fd].path_remove_directory(path);
|
|
894
|
+
} else {
|
|
895
|
+
return ERRNO_BADF;
|
|
896
|
+
}
|
|
897
|
+
}, path_rename(fd, old_path_ptr, old_path_len, new_fd, new_path_ptr, new_path_len) {
|
|
898
|
+
const buffer8 = new Uint8Array(self.inst.exports.memory.buffer);
|
|
899
|
+
if (self.fds[fd] != void 0 && self.fds[new_fd] != void 0) {
|
|
900
|
+
const old_path = new TextDecoder("utf-8").decode(buffer8.slice(old_path_ptr, old_path_ptr + old_path_len));
|
|
901
|
+
const new_path = new TextDecoder("utf-8").decode(buffer8.slice(new_path_ptr, new_path_ptr + new_path_len));
|
|
902
|
+
let { ret, inode_obj } = self.fds[fd].path_unlink(old_path);
|
|
903
|
+
if (inode_obj == null) {
|
|
904
|
+
return ret;
|
|
905
|
+
}
|
|
906
|
+
ret = self.fds[new_fd].path_link(new_path, inode_obj, true);
|
|
907
|
+
if (ret != ERRNO_SUCCESS) {
|
|
908
|
+
if (self.fds[fd].path_link(old_path, inode_obj, true) != ERRNO_SUCCESS) {
|
|
909
|
+
throw "path_link should always return success when relinking an inode back to the original place";
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
return ret;
|
|
913
|
+
} else {
|
|
914
|
+
return ERRNO_BADF;
|
|
915
|
+
}
|
|
916
|
+
}, path_symlink(old_path_ptr, old_path_len, fd, new_path_ptr, new_path_len) {
|
|
917
|
+
const buffer8 = new Uint8Array(self.inst.exports.memory.buffer);
|
|
918
|
+
if (self.fds[fd] != void 0) {
|
|
919
|
+
const old_path = new TextDecoder("utf-8").decode(buffer8.slice(old_path_ptr, old_path_ptr + old_path_len));
|
|
920
|
+
const new_path = new TextDecoder("utf-8").decode(buffer8.slice(new_path_ptr, new_path_ptr + new_path_len));
|
|
921
|
+
return ERRNO_NOTSUP;
|
|
922
|
+
} else {
|
|
923
|
+
return ERRNO_BADF;
|
|
924
|
+
}
|
|
925
|
+
}, path_unlink_file(fd, path_ptr, path_len) {
|
|
926
|
+
const buffer8 = new Uint8Array(self.inst.exports.memory.buffer);
|
|
927
|
+
if (self.fds[fd] != void 0) {
|
|
928
|
+
const path = new TextDecoder("utf-8").decode(buffer8.slice(path_ptr, path_ptr + path_len));
|
|
929
|
+
return self.fds[fd].path_unlink_file(path);
|
|
930
|
+
} else {
|
|
931
|
+
return ERRNO_BADF;
|
|
932
|
+
}
|
|
933
|
+
}, poll_oneoff(in_ptr, out_ptr, nsubscriptions) {
|
|
934
|
+
if (nsubscriptions === 0) {
|
|
935
|
+
return ERRNO_INVAL;
|
|
936
|
+
}
|
|
937
|
+
if (nsubscriptions > 1) {
|
|
938
|
+
debug.log("poll_oneoff: only a single subscription is supported");
|
|
939
|
+
return ERRNO_NOTSUP;
|
|
940
|
+
}
|
|
941
|
+
const buffer = new DataView(self.inst.exports.memory.buffer);
|
|
942
|
+
const s = Subscription.read_bytes(buffer, in_ptr);
|
|
943
|
+
const eventtype = s.eventtype;
|
|
944
|
+
const clockid = s.clockid;
|
|
945
|
+
const timeout = s.timeout;
|
|
946
|
+
if (eventtype !== EVENTTYPE_CLOCK) {
|
|
947
|
+
debug.log("poll_oneoff: only clock subscriptions are supported");
|
|
948
|
+
return ERRNO_NOTSUP;
|
|
949
|
+
}
|
|
950
|
+
let getNow = void 0;
|
|
951
|
+
if (clockid === CLOCKID_MONOTONIC) {
|
|
952
|
+
getNow = () => BigInt(Math.round(performance.now() * 1e6));
|
|
953
|
+
} else if (clockid === CLOCKID_REALTIME) {
|
|
954
|
+
getNow = () => BigInt((/* @__PURE__ */ new Date()).getTime()) * 1000000n;
|
|
955
|
+
} else {
|
|
956
|
+
return ERRNO_INVAL;
|
|
957
|
+
}
|
|
958
|
+
const endTime = (s.flags & SUBCLOCKFLAGS_SUBSCRIPTION_CLOCK_ABSTIME) !== 0 ? timeout : getNow() + timeout;
|
|
959
|
+
while (endTime > getNow()) {
|
|
960
|
+
}
|
|
961
|
+
const event = new Event(s.userdata, ERRNO_SUCCESS, eventtype);
|
|
962
|
+
event.write_bytes(buffer, out_ptr);
|
|
963
|
+
return ERRNO_SUCCESS;
|
|
964
|
+
}, proc_exit(exit_code) {
|
|
965
|
+
throw new WASIProcExit(exit_code);
|
|
966
|
+
}, proc_raise(sig) {
|
|
967
|
+
throw "raised signal " + sig;
|
|
968
|
+
}, sched_yield() {
|
|
969
|
+
}, random_get(buf, buf_len) {
|
|
970
|
+
const buffer8 = new Uint8Array(self.inst.exports.memory.buffer).subarray(buf, buf + buf_len);
|
|
971
|
+
if ("crypto" in globalThis && (typeof SharedArrayBuffer === "undefined" || !(self.inst.exports.memory.buffer instanceof SharedArrayBuffer))) {
|
|
972
|
+
for (let i = 0; i < buf_len; i += 65536) {
|
|
973
|
+
crypto.getRandomValues(buffer8.subarray(i, i + 65536));
|
|
974
|
+
}
|
|
975
|
+
} else {
|
|
976
|
+
for (let i = 0; i < buf_len; i++) {
|
|
977
|
+
buffer8[i] = Math.random() * 256 | 0;
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
}, sock_recv(fd, ri_data, ri_flags) {
|
|
981
|
+
throw "sockets not supported";
|
|
982
|
+
}, sock_send(fd, si_data, si_flags) {
|
|
983
|
+
throw "sockets not supported";
|
|
984
|
+
}, sock_shutdown(fd, how) {
|
|
985
|
+
throw "sockets not supported";
|
|
986
|
+
}, sock_accept(fd, flags) {
|
|
987
|
+
throw "sockets not supported";
|
|
988
|
+
} };
|
|
989
|
+
}
|
|
990
|
+
};
|
|
991
|
+
|
|
992
|
+
// ../../node_modules/@bjorn3/browser_wasi_shim/dist/fd.js
|
|
993
|
+
var Fd = class {
|
|
994
|
+
fd_allocate(offset, len) {
|
|
995
|
+
return ERRNO_NOTSUP;
|
|
996
|
+
}
|
|
997
|
+
fd_close() {
|
|
998
|
+
return 0;
|
|
999
|
+
}
|
|
1000
|
+
fd_fdstat_get() {
|
|
1001
|
+
return { ret: ERRNO_NOTSUP, fdstat: null };
|
|
1002
|
+
}
|
|
1003
|
+
fd_fdstat_set_flags(flags) {
|
|
1004
|
+
return ERRNO_NOTSUP;
|
|
1005
|
+
}
|
|
1006
|
+
fd_fdstat_set_rights(fs_rights_base, fs_rights_inheriting) {
|
|
1007
|
+
return ERRNO_NOTSUP;
|
|
1008
|
+
}
|
|
1009
|
+
fd_filestat_get() {
|
|
1010
|
+
return { ret: ERRNO_NOTSUP, filestat: null };
|
|
1011
|
+
}
|
|
1012
|
+
fd_filestat_set_size(size) {
|
|
1013
|
+
return ERRNO_NOTSUP;
|
|
1014
|
+
}
|
|
1015
|
+
fd_filestat_set_times(atim, mtim, fst_flags) {
|
|
1016
|
+
return ERRNO_NOTSUP;
|
|
1017
|
+
}
|
|
1018
|
+
fd_pread(size, offset) {
|
|
1019
|
+
return { ret: ERRNO_NOTSUP, data: new Uint8Array() };
|
|
1020
|
+
}
|
|
1021
|
+
fd_prestat_get() {
|
|
1022
|
+
return { ret: ERRNO_NOTSUP, prestat: null };
|
|
1023
|
+
}
|
|
1024
|
+
fd_pwrite(data, offset) {
|
|
1025
|
+
return { ret: ERRNO_NOTSUP, nwritten: 0 };
|
|
1026
|
+
}
|
|
1027
|
+
fd_read(size) {
|
|
1028
|
+
return { ret: ERRNO_NOTSUP, data: new Uint8Array() };
|
|
1029
|
+
}
|
|
1030
|
+
fd_readdir_single(cookie) {
|
|
1031
|
+
return { ret: ERRNO_NOTSUP, dirent: null };
|
|
1032
|
+
}
|
|
1033
|
+
fd_seek(offset, whence) {
|
|
1034
|
+
return { ret: ERRNO_NOTSUP, offset: 0n };
|
|
1035
|
+
}
|
|
1036
|
+
fd_sync() {
|
|
1037
|
+
return 0;
|
|
1038
|
+
}
|
|
1039
|
+
fd_tell() {
|
|
1040
|
+
return { ret: ERRNO_NOTSUP, offset: 0n };
|
|
1041
|
+
}
|
|
1042
|
+
fd_write(data) {
|
|
1043
|
+
return { ret: ERRNO_NOTSUP, nwritten: 0 };
|
|
1044
|
+
}
|
|
1045
|
+
path_create_directory(path) {
|
|
1046
|
+
return ERRNO_NOTSUP;
|
|
1047
|
+
}
|
|
1048
|
+
path_filestat_get(flags, path) {
|
|
1049
|
+
return { ret: ERRNO_NOTSUP, filestat: null };
|
|
1050
|
+
}
|
|
1051
|
+
path_filestat_set_times(flags, path, atim, mtim, fst_flags) {
|
|
1052
|
+
return ERRNO_NOTSUP;
|
|
1053
|
+
}
|
|
1054
|
+
path_link(path, inode, allow_dir) {
|
|
1055
|
+
return ERRNO_NOTSUP;
|
|
1056
|
+
}
|
|
1057
|
+
path_unlink(path) {
|
|
1058
|
+
return { ret: ERRNO_NOTSUP, inode_obj: null };
|
|
1059
|
+
}
|
|
1060
|
+
path_lookup(path, dirflags) {
|
|
1061
|
+
return { ret: ERRNO_NOTSUP, inode_obj: null };
|
|
1062
|
+
}
|
|
1063
|
+
path_open(dirflags, path, oflags, fs_rights_base, fs_rights_inheriting, fd_flags) {
|
|
1064
|
+
return { ret: ERRNO_NOTDIR, fd_obj: null };
|
|
1065
|
+
}
|
|
1066
|
+
path_readlink(path) {
|
|
1067
|
+
return { ret: ERRNO_NOTSUP, data: null };
|
|
1068
|
+
}
|
|
1069
|
+
path_remove_directory(path) {
|
|
1070
|
+
return ERRNO_NOTSUP;
|
|
1071
|
+
}
|
|
1072
|
+
path_rename(old_path, new_fd, new_path) {
|
|
1073
|
+
return ERRNO_NOTSUP;
|
|
1074
|
+
}
|
|
1075
|
+
path_unlink_file(path) {
|
|
1076
|
+
return ERRNO_NOTSUP;
|
|
1077
|
+
}
|
|
1078
|
+
};
|
|
1079
|
+
var Inode = class _Inode {
|
|
1080
|
+
static issue_ino() {
|
|
1081
|
+
return _Inode.next_ino++;
|
|
1082
|
+
}
|
|
1083
|
+
static root_ino() {
|
|
1084
|
+
return 0n;
|
|
1085
|
+
}
|
|
1086
|
+
constructor() {
|
|
1087
|
+
this.ino = _Inode.issue_ino();
|
|
1088
|
+
}
|
|
1089
|
+
};
|
|
1090
|
+
Inode.next_ino = 1n;
|
|
1091
|
+
|
|
1092
|
+
// ../../node_modules/@bjorn3/browser_wasi_shim/dist/fs_mem.js
|
|
1093
|
+
var OpenFile = class extends Fd {
|
|
1094
|
+
fd_allocate(offset, len) {
|
|
1095
|
+
if (this.file.size > offset + len) {
|
|
1096
|
+
} else {
|
|
1097
|
+
const new_data = new Uint8Array(Number(offset + len));
|
|
1098
|
+
new_data.set(this.file.data, 0);
|
|
1099
|
+
this.file.data = new_data;
|
|
1100
|
+
}
|
|
1101
|
+
return ERRNO_SUCCESS;
|
|
1102
|
+
}
|
|
1103
|
+
fd_fdstat_get() {
|
|
1104
|
+
return { ret: 0, fdstat: new Fdstat(FILETYPE_REGULAR_FILE, 0) };
|
|
1105
|
+
}
|
|
1106
|
+
fd_filestat_set_size(size) {
|
|
1107
|
+
if (this.file.size > size) {
|
|
1108
|
+
this.file.data = new Uint8Array(this.file.data.buffer.slice(0, Number(size)));
|
|
1109
|
+
} else {
|
|
1110
|
+
const new_data = new Uint8Array(Number(size));
|
|
1111
|
+
new_data.set(this.file.data, 0);
|
|
1112
|
+
this.file.data = new_data;
|
|
1113
|
+
}
|
|
1114
|
+
return ERRNO_SUCCESS;
|
|
1115
|
+
}
|
|
1116
|
+
fd_read(size) {
|
|
1117
|
+
const slice = this.file.data.slice(Number(this.file_pos), Number(this.file_pos + BigInt(size)));
|
|
1118
|
+
this.file_pos += BigInt(slice.length);
|
|
1119
|
+
return { ret: 0, data: slice };
|
|
1120
|
+
}
|
|
1121
|
+
fd_pread(size, offset) {
|
|
1122
|
+
const slice = this.file.data.slice(Number(offset), Number(offset + BigInt(size)));
|
|
1123
|
+
return { ret: 0, data: slice };
|
|
1124
|
+
}
|
|
1125
|
+
fd_seek(offset, whence) {
|
|
1126
|
+
let calculated_offset;
|
|
1127
|
+
switch (whence) {
|
|
1128
|
+
case WHENCE_SET:
|
|
1129
|
+
calculated_offset = offset;
|
|
1130
|
+
break;
|
|
1131
|
+
case WHENCE_CUR:
|
|
1132
|
+
calculated_offset = this.file_pos + offset;
|
|
1133
|
+
break;
|
|
1134
|
+
case WHENCE_END:
|
|
1135
|
+
calculated_offset = BigInt(this.file.data.byteLength) + offset;
|
|
1136
|
+
break;
|
|
1137
|
+
default:
|
|
1138
|
+
return { ret: ERRNO_INVAL, offset: 0n };
|
|
1139
|
+
}
|
|
1140
|
+
if (calculated_offset < 0) {
|
|
1141
|
+
return { ret: ERRNO_INVAL, offset: 0n };
|
|
1142
|
+
}
|
|
1143
|
+
this.file_pos = calculated_offset;
|
|
1144
|
+
return { ret: 0, offset: this.file_pos };
|
|
1145
|
+
}
|
|
1146
|
+
fd_tell() {
|
|
1147
|
+
return { ret: 0, offset: this.file_pos };
|
|
1148
|
+
}
|
|
1149
|
+
fd_write(data) {
|
|
1150
|
+
if (this.file.readonly) return { ret: ERRNO_BADF, nwritten: 0 };
|
|
1151
|
+
if (this.file_pos + BigInt(data.byteLength) > this.file.size) {
|
|
1152
|
+
const old = this.file.data;
|
|
1153
|
+
this.file.data = new Uint8Array(Number(this.file_pos + BigInt(data.byteLength)));
|
|
1154
|
+
this.file.data.set(old);
|
|
1155
|
+
}
|
|
1156
|
+
this.file.data.set(data, Number(this.file_pos));
|
|
1157
|
+
this.file_pos += BigInt(data.byteLength);
|
|
1158
|
+
return { ret: 0, nwritten: data.byteLength };
|
|
1159
|
+
}
|
|
1160
|
+
fd_pwrite(data, offset) {
|
|
1161
|
+
if (this.file.readonly) return { ret: ERRNO_BADF, nwritten: 0 };
|
|
1162
|
+
if (offset + BigInt(data.byteLength) > this.file.size) {
|
|
1163
|
+
const old = this.file.data;
|
|
1164
|
+
this.file.data = new Uint8Array(Number(offset + BigInt(data.byteLength)));
|
|
1165
|
+
this.file.data.set(old);
|
|
1166
|
+
}
|
|
1167
|
+
this.file.data.set(data, Number(offset));
|
|
1168
|
+
return { ret: 0, nwritten: data.byteLength };
|
|
1169
|
+
}
|
|
1170
|
+
fd_filestat_get() {
|
|
1171
|
+
return { ret: 0, filestat: this.file.stat() };
|
|
1172
|
+
}
|
|
1173
|
+
constructor(file) {
|
|
1174
|
+
super();
|
|
1175
|
+
this.file_pos = 0n;
|
|
1176
|
+
this.file = file;
|
|
1177
|
+
}
|
|
1178
|
+
};
|
|
1179
|
+
var OpenDirectory = class extends Fd {
|
|
1180
|
+
fd_seek(offset, whence) {
|
|
1181
|
+
return { ret: ERRNO_BADF, offset: 0n };
|
|
1182
|
+
}
|
|
1183
|
+
fd_tell() {
|
|
1184
|
+
return { ret: ERRNO_BADF, offset: 0n };
|
|
1185
|
+
}
|
|
1186
|
+
fd_allocate(offset, len) {
|
|
1187
|
+
return ERRNO_BADF;
|
|
1188
|
+
}
|
|
1189
|
+
fd_fdstat_get() {
|
|
1190
|
+
return { ret: 0, fdstat: new Fdstat(FILETYPE_DIRECTORY, 0) };
|
|
1191
|
+
}
|
|
1192
|
+
fd_readdir_single(cookie) {
|
|
1193
|
+
if (debug.enabled) {
|
|
1194
|
+
debug.log("readdir_single", cookie);
|
|
1195
|
+
debug.log(cookie, this.dir.contents.keys());
|
|
1196
|
+
}
|
|
1197
|
+
if (cookie == 0n) {
|
|
1198
|
+
return { ret: ERRNO_SUCCESS, dirent: new Dirent(1n, this.dir.ino, ".", FILETYPE_DIRECTORY) };
|
|
1199
|
+
} else if (cookie == 1n) {
|
|
1200
|
+
return { ret: ERRNO_SUCCESS, dirent: new Dirent(2n, this.dir.parent_ino(), "..", FILETYPE_DIRECTORY) };
|
|
1201
|
+
}
|
|
1202
|
+
if (cookie >= BigInt(this.dir.contents.size) + 2n) {
|
|
1203
|
+
return { ret: 0, dirent: null };
|
|
1204
|
+
}
|
|
1205
|
+
const [name, entry] = Array.from(this.dir.contents.entries())[Number(cookie - 2n)];
|
|
1206
|
+
return { ret: 0, dirent: new Dirent(cookie + 1n, entry.ino, name, entry.stat().filetype) };
|
|
1207
|
+
}
|
|
1208
|
+
path_filestat_get(flags, path_str) {
|
|
1209
|
+
const { ret: path_err, path } = Path.from(path_str);
|
|
1210
|
+
if (path == null) {
|
|
1211
|
+
return { ret: path_err, filestat: null };
|
|
1212
|
+
}
|
|
1213
|
+
const { ret, entry } = this.dir.get_entry_for_path(path);
|
|
1214
|
+
if (entry == null) {
|
|
1215
|
+
return { ret, filestat: null };
|
|
1216
|
+
}
|
|
1217
|
+
return { ret: 0, filestat: entry.stat() };
|
|
1218
|
+
}
|
|
1219
|
+
path_lookup(path_str, dirflags) {
|
|
1220
|
+
const { ret: path_ret, path } = Path.from(path_str);
|
|
1221
|
+
if (path == null) {
|
|
1222
|
+
return { ret: path_ret, inode_obj: null };
|
|
1223
|
+
}
|
|
1224
|
+
const { ret, entry } = this.dir.get_entry_for_path(path);
|
|
1225
|
+
if (entry == null) {
|
|
1226
|
+
return { ret, inode_obj: null };
|
|
1227
|
+
}
|
|
1228
|
+
return { ret: ERRNO_SUCCESS, inode_obj: entry };
|
|
1229
|
+
}
|
|
1230
|
+
path_open(dirflags, path_str, oflags, fs_rights_base, fs_rights_inheriting, fd_flags) {
|
|
1231
|
+
const { ret: path_ret, path } = Path.from(path_str);
|
|
1232
|
+
if (path == null) {
|
|
1233
|
+
return { ret: path_ret, fd_obj: null };
|
|
1234
|
+
}
|
|
1235
|
+
let { ret, entry } = this.dir.get_entry_for_path(path);
|
|
1236
|
+
if (entry == null) {
|
|
1237
|
+
if (ret != ERRNO_NOENT) {
|
|
1238
|
+
return { ret, fd_obj: null };
|
|
1239
|
+
}
|
|
1240
|
+
if ((oflags & OFLAGS_CREAT) == OFLAGS_CREAT) {
|
|
1241
|
+
const { ret: ret2, entry: new_entry } = this.dir.create_entry_for_path(path_str, (oflags & OFLAGS_DIRECTORY) == OFLAGS_DIRECTORY);
|
|
1242
|
+
if (new_entry == null) {
|
|
1243
|
+
return { ret: ret2, fd_obj: null };
|
|
1244
|
+
}
|
|
1245
|
+
entry = new_entry;
|
|
1246
|
+
} else {
|
|
1247
|
+
return { ret: ERRNO_NOENT, fd_obj: null };
|
|
1248
|
+
}
|
|
1249
|
+
} else if ((oflags & OFLAGS_EXCL) == OFLAGS_EXCL) {
|
|
1250
|
+
return { ret: ERRNO_EXIST, fd_obj: null };
|
|
1251
|
+
}
|
|
1252
|
+
if ((oflags & OFLAGS_DIRECTORY) == OFLAGS_DIRECTORY && entry.stat().filetype !== FILETYPE_DIRECTORY) {
|
|
1253
|
+
return { ret: ERRNO_NOTDIR, fd_obj: null };
|
|
1254
|
+
}
|
|
1255
|
+
return entry.path_open(oflags, fs_rights_base, fd_flags);
|
|
1256
|
+
}
|
|
1257
|
+
path_create_directory(path) {
|
|
1258
|
+
return this.path_open(0, path, OFLAGS_CREAT | OFLAGS_DIRECTORY, 0n, 0n, 0).ret;
|
|
1259
|
+
}
|
|
1260
|
+
path_link(path_str, inode, allow_dir) {
|
|
1261
|
+
const { ret: path_ret, path } = Path.from(path_str);
|
|
1262
|
+
if (path == null) {
|
|
1263
|
+
return path_ret;
|
|
1264
|
+
}
|
|
1265
|
+
if (path.is_dir) {
|
|
1266
|
+
return ERRNO_NOENT;
|
|
1267
|
+
}
|
|
1268
|
+
const { ret: parent_ret, parent_entry, filename, entry } = this.dir.get_parent_dir_and_entry_for_path(path, true);
|
|
1269
|
+
if (parent_entry == null || filename == null) {
|
|
1270
|
+
return parent_ret;
|
|
1271
|
+
}
|
|
1272
|
+
if (entry != null) {
|
|
1273
|
+
const source_is_dir = inode.stat().filetype == FILETYPE_DIRECTORY;
|
|
1274
|
+
const target_is_dir = entry.stat().filetype == FILETYPE_DIRECTORY;
|
|
1275
|
+
if (source_is_dir && target_is_dir) {
|
|
1276
|
+
if (allow_dir && entry instanceof Directory) {
|
|
1277
|
+
if (entry.contents.size == 0) {
|
|
1278
|
+
} else {
|
|
1279
|
+
return ERRNO_NOTEMPTY;
|
|
1280
|
+
}
|
|
1281
|
+
} else {
|
|
1282
|
+
return ERRNO_EXIST;
|
|
1283
|
+
}
|
|
1284
|
+
} else if (source_is_dir && !target_is_dir) {
|
|
1285
|
+
return ERRNO_NOTDIR;
|
|
1286
|
+
} else if (!source_is_dir && target_is_dir) {
|
|
1287
|
+
return ERRNO_ISDIR;
|
|
1288
|
+
} else if (inode.stat().filetype == FILETYPE_REGULAR_FILE && entry.stat().filetype == FILETYPE_REGULAR_FILE) {
|
|
1289
|
+
} else {
|
|
1290
|
+
return ERRNO_EXIST;
|
|
1291
|
+
}
|
|
1292
|
+
}
|
|
1293
|
+
if (!allow_dir && inode.stat().filetype == FILETYPE_DIRECTORY) {
|
|
1294
|
+
return ERRNO_PERM;
|
|
1295
|
+
}
|
|
1296
|
+
parent_entry.contents.set(filename, inode);
|
|
1297
|
+
return ERRNO_SUCCESS;
|
|
1298
|
+
}
|
|
1299
|
+
path_unlink(path_str) {
|
|
1300
|
+
const { ret: path_ret, path } = Path.from(path_str);
|
|
1301
|
+
if (path == null) {
|
|
1302
|
+
return { ret: path_ret, inode_obj: null };
|
|
1303
|
+
}
|
|
1304
|
+
const { ret: parent_ret, parent_entry, filename, entry } = this.dir.get_parent_dir_and_entry_for_path(path, true);
|
|
1305
|
+
if (parent_entry == null || filename == null) {
|
|
1306
|
+
return { ret: parent_ret, inode_obj: null };
|
|
1307
|
+
}
|
|
1308
|
+
if (entry == null) {
|
|
1309
|
+
return { ret: ERRNO_NOENT, inode_obj: null };
|
|
1310
|
+
}
|
|
1311
|
+
parent_entry.contents.delete(filename);
|
|
1312
|
+
return { ret: ERRNO_SUCCESS, inode_obj: entry };
|
|
1313
|
+
}
|
|
1314
|
+
path_unlink_file(path_str) {
|
|
1315
|
+
const { ret: path_ret, path } = Path.from(path_str);
|
|
1316
|
+
if (path == null) {
|
|
1317
|
+
return path_ret;
|
|
1318
|
+
}
|
|
1319
|
+
const { ret: parent_ret, parent_entry, filename, entry } = this.dir.get_parent_dir_and_entry_for_path(path, false);
|
|
1320
|
+
if (parent_entry == null || filename == null || entry == null) {
|
|
1321
|
+
return parent_ret;
|
|
1322
|
+
}
|
|
1323
|
+
if (entry.stat().filetype === FILETYPE_DIRECTORY) {
|
|
1324
|
+
return ERRNO_ISDIR;
|
|
1325
|
+
}
|
|
1326
|
+
parent_entry.contents.delete(filename);
|
|
1327
|
+
return ERRNO_SUCCESS;
|
|
1328
|
+
}
|
|
1329
|
+
path_remove_directory(path_str) {
|
|
1330
|
+
const { ret: path_ret, path } = Path.from(path_str);
|
|
1331
|
+
if (path == null) {
|
|
1332
|
+
return path_ret;
|
|
1333
|
+
}
|
|
1334
|
+
const { ret: parent_ret, parent_entry, filename, entry } = this.dir.get_parent_dir_and_entry_for_path(path, false);
|
|
1335
|
+
if (parent_entry == null || filename == null || entry == null) {
|
|
1336
|
+
return parent_ret;
|
|
1337
|
+
}
|
|
1338
|
+
if (!(entry instanceof Directory) || entry.stat().filetype !== FILETYPE_DIRECTORY) {
|
|
1339
|
+
return ERRNO_NOTDIR;
|
|
1340
|
+
}
|
|
1341
|
+
if (entry.contents.size !== 0) {
|
|
1342
|
+
return ERRNO_NOTEMPTY;
|
|
1343
|
+
}
|
|
1344
|
+
if (!parent_entry.contents.delete(filename)) {
|
|
1345
|
+
return ERRNO_NOENT;
|
|
1346
|
+
}
|
|
1347
|
+
return ERRNO_SUCCESS;
|
|
1348
|
+
}
|
|
1349
|
+
fd_filestat_get() {
|
|
1350
|
+
return { ret: 0, filestat: this.dir.stat() };
|
|
1351
|
+
}
|
|
1352
|
+
fd_filestat_set_size(size) {
|
|
1353
|
+
return ERRNO_BADF;
|
|
1354
|
+
}
|
|
1355
|
+
fd_read(size) {
|
|
1356
|
+
return { ret: ERRNO_BADF, data: new Uint8Array() };
|
|
1357
|
+
}
|
|
1358
|
+
fd_pread(size, offset) {
|
|
1359
|
+
return { ret: ERRNO_BADF, data: new Uint8Array() };
|
|
1360
|
+
}
|
|
1361
|
+
fd_write(data) {
|
|
1362
|
+
return { ret: ERRNO_BADF, nwritten: 0 };
|
|
1363
|
+
}
|
|
1364
|
+
fd_pwrite(data, offset) {
|
|
1365
|
+
return { ret: ERRNO_BADF, nwritten: 0 };
|
|
1366
|
+
}
|
|
1367
|
+
constructor(dir) {
|
|
1368
|
+
super();
|
|
1369
|
+
this.dir = dir;
|
|
1370
|
+
}
|
|
1371
|
+
};
|
|
1372
|
+
var PreopenDirectory = class extends OpenDirectory {
|
|
1373
|
+
fd_prestat_get() {
|
|
1374
|
+
return { ret: 0, prestat: Prestat.dir(this.prestat_name) };
|
|
1375
|
+
}
|
|
1376
|
+
constructor(name, contents) {
|
|
1377
|
+
super(new Directory(contents));
|
|
1378
|
+
this.prestat_name = name;
|
|
1379
|
+
}
|
|
1380
|
+
};
|
|
1381
|
+
var File = class extends Inode {
|
|
1382
|
+
path_open(oflags, fs_rights_base, fd_flags) {
|
|
1383
|
+
if (this.readonly && (fs_rights_base & BigInt(RIGHTS_FD_WRITE)) == BigInt(RIGHTS_FD_WRITE)) {
|
|
1384
|
+
return { ret: ERRNO_PERM, fd_obj: null };
|
|
1385
|
+
}
|
|
1386
|
+
if ((oflags & OFLAGS_TRUNC) == OFLAGS_TRUNC) {
|
|
1387
|
+
if (this.readonly) return { ret: ERRNO_PERM, fd_obj: null };
|
|
1388
|
+
this.data = new Uint8Array([]);
|
|
1389
|
+
}
|
|
1390
|
+
const file = new OpenFile(this);
|
|
1391
|
+
if (fd_flags & FDFLAGS_APPEND) file.fd_seek(0n, WHENCE_END);
|
|
1392
|
+
return { ret: ERRNO_SUCCESS, fd_obj: file };
|
|
1393
|
+
}
|
|
1394
|
+
get size() {
|
|
1395
|
+
return BigInt(this.data.byteLength);
|
|
1396
|
+
}
|
|
1397
|
+
stat() {
|
|
1398
|
+
return new Filestat(this.ino, FILETYPE_REGULAR_FILE, this.size);
|
|
1399
|
+
}
|
|
1400
|
+
constructor(data, options) {
|
|
1401
|
+
super();
|
|
1402
|
+
this.data = new Uint8Array(data);
|
|
1403
|
+
this.readonly = !!options?.readonly;
|
|
1404
|
+
}
|
|
1405
|
+
};
|
|
1406
|
+
var Path = class Path2 {
|
|
1407
|
+
static from(path) {
|
|
1408
|
+
const self = new Path2();
|
|
1409
|
+
self.is_dir = path.endsWith("/");
|
|
1410
|
+
if (path.startsWith("/")) {
|
|
1411
|
+
return { ret: ERRNO_NOTCAPABLE, path: null };
|
|
1412
|
+
}
|
|
1413
|
+
if (path.includes("\0")) {
|
|
1414
|
+
return { ret: ERRNO_INVAL, path: null };
|
|
1415
|
+
}
|
|
1416
|
+
for (const component of path.split("/")) {
|
|
1417
|
+
if (component === "" || component === ".") {
|
|
1418
|
+
continue;
|
|
1419
|
+
}
|
|
1420
|
+
if (component === "..") {
|
|
1421
|
+
if (self.parts.pop() == void 0) {
|
|
1422
|
+
return { ret: ERRNO_NOTCAPABLE, path: null };
|
|
1423
|
+
}
|
|
1424
|
+
continue;
|
|
1425
|
+
}
|
|
1426
|
+
self.parts.push(component);
|
|
1427
|
+
}
|
|
1428
|
+
return { ret: ERRNO_SUCCESS, path: self };
|
|
1429
|
+
}
|
|
1430
|
+
to_path_string() {
|
|
1431
|
+
let s = this.parts.join("/");
|
|
1432
|
+
if (this.is_dir) {
|
|
1433
|
+
s += "/";
|
|
1434
|
+
}
|
|
1435
|
+
return s;
|
|
1436
|
+
}
|
|
1437
|
+
constructor() {
|
|
1438
|
+
this.parts = [];
|
|
1439
|
+
this.is_dir = false;
|
|
1440
|
+
}
|
|
1441
|
+
};
|
|
1442
|
+
var Directory = class _Directory extends Inode {
|
|
1443
|
+
parent_ino() {
|
|
1444
|
+
if (this.parent == null) {
|
|
1445
|
+
return Inode.root_ino();
|
|
1446
|
+
}
|
|
1447
|
+
return this.parent.ino;
|
|
1448
|
+
}
|
|
1449
|
+
path_open(oflags, fs_rights_base, fd_flags) {
|
|
1450
|
+
return { ret: ERRNO_SUCCESS, fd_obj: new OpenDirectory(this) };
|
|
1451
|
+
}
|
|
1452
|
+
stat() {
|
|
1453
|
+
return new Filestat(this.ino, FILETYPE_DIRECTORY, 0n);
|
|
1454
|
+
}
|
|
1455
|
+
get_entry_for_path(path) {
|
|
1456
|
+
let entry = this;
|
|
1457
|
+
for (const component of path.parts) {
|
|
1458
|
+
if (!(entry instanceof _Directory)) {
|
|
1459
|
+
return { ret: ERRNO_NOTDIR, entry: null };
|
|
1460
|
+
}
|
|
1461
|
+
const child = entry.contents.get(component);
|
|
1462
|
+
if (child !== void 0) {
|
|
1463
|
+
entry = child;
|
|
1464
|
+
} else {
|
|
1465
|
+
debug.log(component);
|
|
1466
|
+
return { ret: ERRNO_NOENT, entry: null };
|
|
1467
|
+
}
|
|
1468
|
+
}
|
|
1469
|
+
if (path.is_dir) {
|
|
1470
|
+
if (entry.stat().filetype != FILETYPE_DIRECTORY) {
|
|
1471
|
+
return { ret: ERRNO_NOTDIR, entry: null };
|
|
1472
|
+
}
|
|
1473
|
+
}
|
|
1474
|
+
return { ret: ERRNO_SUCCESS, entry };
|
|
1475
|
+
}
|
|
1476
|
+
get_parent_dir_and_entry_for_path(path, allow_undefined) {
|
|
1477
|
+
const filename = path.parts.pop();
|
|
1478
|
+
if (filename === void 0) {
|
|
1479
|
+
return { ret: ERRNO_INVAL, parent_entry: null, filename: null, entry: null };
|
|
1480
|
+
}
|
|
1481
|
+
const { ret: entry_ret, entry: parent_entry } = this.get_entry_for_path(path);
|
|
1482
|
+
if (parent_entry == null) {
|
|
1483
|
+
return { ret: entry_ret, parent_entry: null, filename: null, entry: null };
|
|
1484
|
+
}
|
|
1485
|
+
if (!(parent_entry instanceof _Directory)) {
|
|
1486
|
+
return { ret: ERRNO_NOTDIR, parent_entry: null, filename: null, entry: null };
|
|
1487
|
+
}
|
|
1488
|
+
const entry = parent_entry.contents.get(filename);
|
|
1489
|
+
if (entry === void 0) {
|
|
1490
|
+
if (!allow_undefined) {
|
|
1491
|
+
return { ret: ERRNO_NOENT, parent_entry: null, filename: null, entry: null };
|
|
1492
|
+
} else {
|
|
1493
|
+
return { ret: ERRNO_SUCCESS, parent_entry, filename, entry: null };
|
|
1494
|
+
}
|
|
1495
|
+
}
|
|
1496
|
+
if (path.is_dir) {
|
|
1497
|
+
if (entry.stat().filetype != FILETYPE_DIRECTORY) {
|
|
1498
|
+
return { ret: ERRNO_NOTDIR, parent_entry: null, filename: null, entry: null };
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
1501
|
+
return { ret: ERRNO_SUCCESS, parent_entry, filename, entry };
|
|
1502
|
+
}
|
|
1503
|
+
create_entry_for_path(path_str, is_dir) {
|
|
1504
|
+
const { ret: path_ret, path } = Path.from(path_str);
|
|
1505
|
+
if (path == null) {
|
|
1506
|
+
return { ret: path_ret, entry: null };
|
|
1507
|
+
}
|
|
1508
|
+
let { ret: parent_ret, parent_entry, filename, entry } = this.get_parent_dir_and_entry_for_path(path, true);
|
|
1509
|
+
if (parent_entry == null || filename == null) {
|
|
1510
|
+
return { ret: parent_ret, entry: null };
|
|
1511
|
+
}
|
|
1512
|
+
if (entry != null) {
|
|
1513
|
+
return { ret: ERRNO_EXIST, entry: null };
|
|
1514
|
+
}
|
|
1515
|
+
debug.log("create", path);
|
|
1516
|
+
let new_child;
|
|
1517
|
+
if (!is_dir) {
|
|
1518
|
+
new_child = new File(new ArrayBuffer(0));
|
|
1519
|
+
} else {
|
|
1520
|
+
new_child = new _Directory(/* @__PURE__ */ new Map());
|
|
1521
|
+
}
|
|
1522
|
+
parent_entry.contents.set(filename, new_child);
|
|
1523
|
+
entry = new_child;
|
|
1524
|
+
return { ret: ERRNO_SUCCESS, entry };
|
|
1525
|
+
}
|
|
1526
|
+
constructor(contents) {
|
|
1527
|
+
super();
|
|
1528
|
+
this.parent = null;
|
|
1529
|
+
if (contents instanceof Array) {
|
|
1530
|
+
this.contents = new Map(contents);
|
|
1531
|
+
} else {
|
|
1532
|
+
this.contents = contents;
|
|
1533
|
+
}
|
|
1534
|
+
for (const entry of this.contents.values()) {
|
|
1535
|
+
if (entry instanceof _Directory) {
|
|
1536
|
+
entry.parent = this;
|
|
1537
|
+
}
|
|
1538
|
+
}
|
|
1539
|
+
}
|
|
1540
|
+
};
|
|
1541
|
+
var ConsoleStdout = class _ConsoleStdout extends Fd {
|
|
1542
|
+
fd_filestat_get() {
|
|
1543
|
+
const filestat = new Filestat(this.ino, FILETYPE_CHARACTER_DEVICE, BigInt(0));
|
|
1544
|
+
return { ret: 0, filestat };
|
|
1545
|
+
}
|
|
1546
|
+
fd_fdstat_get() {
|
|
1547
|
+
const fdstat = new Fdstat(FILETYPE_CHARACTER_DEVICE, 0);
|
|
1548
|
+
fdstat.fs_rights_base = BigInt(RIGHTS_FD_WRITE);
|
|
1549
|
+
return { ret: 0, fdstat };
|
|
1550
|
+
}
|
|
1551
|
+
fd_write(data) {
|
|
1552
|
+
this.write(data);
|
|
1553
|
+
return { ret: 0, nwritten: data.byteLength };
|
|
1554
|
+
}
|
|
1555
|
+
static lineBuffered(write) {
|
|
1556
|
+
const dec4 = new TextDecoder("utf-8", { fatal: false });
|
|
1557
|
+
let line_buf = "";
|
|
1558
|
+
return new _ConsoleStdout((buffer) => {
|
|
1559
|
+
line_buf += dec4.decode(buffer, { stream: true });
|
|
1560
|
+
const lines = line_buf.split("\n");
|
|
1561
|
+
for (const [i, line] of lines.entries()) {
|
|
1562
|
+
if (i < lines.length - 1) {
|
|
1563
|
+
write(line);
|
|
1564
|
+
} else {
|
|
1565
|
+
line_buf = line;
|
|
1566
|
+
}
|
|
1567
|
+
}
|
|
1568
|
+
});
|
|
1569
|
+
}
|
|
1570
|
+
constructor(write) {
|
|
1571
|
+
super();
|
|
1572
|
+
this.ino = Inode.issue_ino();
|
|
1573
|
+
this.write = write;
|
|
1574
|
+
}
|
|
1575
|
+
};
|
|
1576
|
+
|
|
1577
|
+
// src/_adapter/git-fs.ts
|
|
1578
|
+
function fsErr(code, p) {
|
|
1579
|
+
const e = new Error(`${code}: ${p}`);
|
|
1580
|
+
e.code = code;
|
|
1581
|
+
return e;
|
|
1582
|
+
}
|
|
1583
|
+
function makeGitFs(preopen, mount, { File: File2, Directory: Directory2 }) {
|
|
1584
|
+
const root = preopen.dir;
|
|
1585
|
+
const isDir = (n) => !!n && n.contents instanceof Map;
|
|
1586
|
+
function parts(p) {
|
|
1587
|
+
if (p === mount) return [];
|
|
1588
|
+
if (!p.startsWith(mount + "/")) throw fsErr("ENOENT", p);
|
|
1589
|
+
const rel = p.slice(mount.length + 1).replace(/\/+$/, "");
|
|
1590
|
+
return rel === "" ? [] : rel.split("/");
|
|
1591
|
+
}
|
|
1592
|
+
function resolve(ps) {
|
|
1593
|
+
let cur = root;
|
|
1594
|
+
for (const part of ps) {
|
|
1595
|
+
if (!isDir(cur)) return null;
|
|
1596
|
+
const next = cur.contents.get(part);
|
|
1597
|
+
if (next === void 0) return null;
|
|
1598
|
+
cur = next;
|
|
1599
|
+
}
|
|
1600
|
+
return cur;
|
|
1601
|
+
}
|
|
1602
|
+
const parent = (ps) => resolve(ps.slice(0, -1));
|
|
1603
|
+
const leaf = (ps) => ps[ps.length - 1];
|
|
1604
|
+
const promises = {
|
|
1605
|
+
async readFile(p, opts) {
|
|
1606
|
+
const n = resolve(parts(p));
|
|
1607
|
+
if (n === null) throw fsErr("ENOENT", p);
|
|
1608
|
+
if (isDir(n)) throw fsErr("EISDIR", p);
|
|
1609
|
+
const data = n.data ?? new Uint8Array(0);
|
|
1610
|
+
const encoding = typeof opts === "string" ? opts : opts && opts.encoding;
|
|
1611
|
+
return encoding ? new TextDecoder().decode(data) : new Uint8Array(data);
|
|
1612
|
+
},
|
|
1613
|
+
async writeFile(p, data) {
|
|
1614
|
+
const ps = parts(p);
|
|
1615
|
+
const par = parent(ps);
|
|
1616
|
+
if (!isDir(par)) throw fsErr("ENOENT", p);
|
|
1617
|
+
const bytes = typeof data === "string" ? new TextEncoder().encode(data) : new Uint8Array(data);
|
|
1618
|
+
par.contents.set(leaf(ps), new File2(bytes));
|
|
1619
|
+
},
|
|
1620
|
+
async unlink(p) {
|
|
1621
|
+
const ps = parts(p), par = parent(ps);
|
|
1622
|
+
if (!isDir(par) || !par.contents.delete(leaf(ps))) throw fsErr("ENOENT", p);
|
|
1623
|
+
},
|
|
1624
|
+
async readdir(p) {
|
|
1625
|
+
const n = resolve(parts(p));
|
|
1626
|
+
if (n === null) throw fsErr("ENOENT", p);
|
|
1627
|
+
if (!isDir(n)) throw fsErr("ENOTDIR", p);
|
|
1628
|
+
return [...n.contents.keys()];
|
|
1629
|
+
},
|
|
1630
|
+
async mkdir(p) {
|
|
1631
|
+
const ps = parts(p), par = parent(ps);
|
|
1632
|
+
if (!isDir(par)) throw fsErr("ENOENT", p);
|
|
1633
|
+
if (par.contents.has(leaf(ps))) throw fsErr("EEXIST", p);
|
|
1634
|
+
par.contents.set(leaf(ps), new Directory2(/* @__PURE__ */ new Map()));
|
|
1635
|
+
},
|
|
1636
|
+
async rmdir(p) {
|
|
1637
|
+
const ps = parts(p), par = parent(ps);
|
|
1638
|
+
if (!isDir(par)) throw fsErr("ENOENT", p);
|
|
1639
|
+
par.contents.delete(leaf(ps));
|
|
1640
|
+
},
|
|
1641
|
+
async stat(p) {
|
|
1642
|
+
return promises.lstat(p);
|
|
1643
|
+
},
|
|
1644
|
+
async lstat(p) {
|
|
1645
|
+
const n = resolve(parts(p));
|
|
1646
|
+
if (n === null) throw fsErr("ENOENT", p);
|
|
1647
|
+
const dir = isDir(n);
|
|
1648
|
+
return {
|
|
1649
|
+
type: dir ? "dir" : "file",
|
|
1650
|
+
mode: dir ? 16384 : 33188,
|
|
1651
|
+
size: dir ? 0 : n.data ? n.data.length : 0,
|
|
1652
|
+
ino: 0,
|
|
1653
|
+
mtimeMs: 0,
|
|
1654
|
+
ctimeMs: 0,
|
|
1655
|
+
uid: 1,
|
|
1656
|
+
gid: 1,
|
|
1657
|
+
dev: 1,
|
|
1658
|
+
isFile: () => !dir,
|
|
1659
|
+
isDirectory: () => dir,
|
|
1660
|
+
isSymbolicLink: () => false
|
|
1661
|
+
};
|
|
1662
|
+
},
|
|
1663
|
+
async readlink(p) {
|
|
1664
|
+
throw fsErr("EINVAL", p);
|
|
1665
|
+
},
|
|
1666
|
+
async symlink() {
|
|
1667
|
+
throw fsErr("EPERM", "symlink unsupported");
|
|
1668
|
+
},
|
|
1669
|
+
async chmod() {
|
|
1670
|
+
}
|
|
1671
|
+
};
|
|
1672
|
+
return { promises };
|
|
1673
|
+
}
|
|
1674
|
+
|
|
1675
|
+
// src/_adapter/tree.ts
|
|
1676
|
+
var enc = new TextEncoder();
|
|
1677
|
+
var dec = new TextDecoder();
|
|
1678
|
+
function flatten(dir, prefix, out) {
|
|
1679
|
+
for (const [name, inode] of dir.contents ?? /* @__PURE__ */ new Map()) {
|
|
1680
|
+
const path = prefix ? prefix + "/" + name : name;
|
|
1681
|
+
if (inode.contents instanceof Map) {
|
|
1682
|
+
out.push({ path, dir: true });
|
|
1683
|
+
flatten(inode, path, out);
|
|
1684
|
+
} else {
|
|
1685
|
+
out.push({ path, dir: false, bytes: inode.data ?? new Uint8Array(0) });
|
|
1686
|
+
}
|
|
1687
|
+
}
|
|
1688
|
+
}
|
|
1689
|
+
function serializeTree(workTree) {
|
|
1690
|
+
const entries = [];
|
|
1691
|
+
flatten(workTree, "", entries);
|
|
1692
|
+
const chunks = [];
|
|
1693
|
+
const header = new Uint8Array(4);
|
|
1694
|
+
new DataView(header.buffer).setUint32(0, entries.length, true);
|
|
1695
|
+
chunks.push(header);
|
|
1696
|
+
for (const e of entries) {
|
|
1697
|
+
const pathBytes = enc.encode(e.path);
|
|
1698
|
+
const body = e.dir ? new Uint8Array(0) : e.bytes ?? new Uint8Array(0);
|
|
1699
|
+
const meta = new Uint8Array(9);
|
|
1700
|
+
const dv = new DataView(meta.buffer);
|
|
1701
|
+
dv.setUint8(0, e.dir ? 1 : 0);
|
|
1702
|
+
dv.setUint32(1, pathBytes.length, true);
|
|
1703
|
+
dv.setUint32(5, body.length, true);
|
|
1704
|
+
chunks.push(meta, pathBytes, body);
|
|
1705
|
+
}
|
|
1706
|
+
let total = 0;
|
|
1707
|
+
for (const c of chunks) total += c.length;
|
|
1708
|
+
const out = new Uint8Array(total);
|
|
1709
|
+
let off = 0;
|
|
1710
|
+
for (const c of chunks) {
|
|
1711
|
+
out.set(c, off);
|
|
1712
|
+
off += c.length;
|
|
1713
|
+
}
|
|
1714
|
+
return out;
|
|
1715
|
+
}
|
|
1716
|
+
|
|
1717
|
+
// src/_adapter/custody-transport.ts
|
|
1718
|
+
var enc2 = new TextEncoder();
|
|
1719
|
+
var dec2 = new TextDecoder();
|
|
1720
|
+
var ZERO_OID = "0".repeat(40);
|
|
1721
|
+
var EMPTY_PACK = new Uint8Array([
|
|
1722
|
+
80,
|
|
1723
|
+
65,
|
|
1724
|
+
67,
|
|
1725
|
+
75,
|
|
1726
|
+
0,
|
|
1727
|
+
0,
|
|
1728
|
+
0,
|
|
1729
|
+
2,
|
|
1730
|
+
0,
|
|
1731
|
+
0,
|
|
1732
|
+
0,
|
|
1733
|
+
0,
|
|
1734
|
+
// "PACK", ver 2, 0 objects
|
|
1735
|
+
2,
|
|
1736
|
+
157,
|
|
1737
|
+
8,
|
|
1738
|
+
130,
|
|
1739
|
+
59,
|
|
1740
|
+
216,
|
|
1741
|
+
168,
|
|
1742
|
+
234,
|
|
1743
|
+
181,
|
|
1744
|
+
16,
|
|
1745
|
+
173,
|
|
1746
|
+
106,
|
|
1747
|
+
// sha1(the 12 header bytes)
|
|
1748
|
+
199,
|
|
1749
|
+
92,
|
|
1750
|
+
130,
|
|
1751
|
+
60,
|
|
1752
|
+
253,
|
|
1753
|
+
62,
|
|
1754
|
+
211,
|
|
1755
|
+
30
|
|
1756
|
+
]);
|
|
1757
|
+
function concatBytes(chunks) {
|
|
1758
|
+
let n = 0;
|
|
1759
|
+
for (const c of chunks) n += c.length;
|
|
1760
|
+
const out = new Uint8Array(n);
|
|
1761
|
+
let o = 0;
|
|
1762
|
+
for (const c of chunks) {
|
|
1763
|
+
out.set(c, o);
|
|
1764
|
+
o += c.length;
|
|
1765
|
+
}
|
|
1766
|
+
return out;
|
|
1767
|
+
}
|
|
1768
|
+
function pkt(payload) {
|
|
1769
|
+
const body = typeof payload === "string" ? enc2.encode(payload) : payload;
|
|
1770
|
+
const len = (body.length + 4).toString(16).padStart(4, "0");
|
|
1771
|
+
return concatBytes([enc2.encode(len), body]);
|
|
1772
|
+
}
|
|
1773
|
+
var FLUSH = enc2.encode("0000");
|
|
1774
|
+
function readPktLines(buf, off = 0) {
|
|
1775
|
+
const lines = [];
|
|
1776
|
+
while (off + 4 <= buf.length) {
|
|
1777
|
+
const hex = dec2.decode(buf.subarray(off, off + 4));
|
|
1778
|
+
if (!/^[0-9a-f]{4}$/.test(hex)) break;
|
|
1779
|
+
const len = parseInt(hex, 16);
|
|
1780
|
+
if (len === 0) {
|
|
1781
|
+
off += 4;
|
|
1782
|
+
break;
|
|
1783
|
+
}
|
|
1784
|
+
if (len < 4 || off + len > buf.length) break;
|
|
1785
|
+
lines.push(buf.subarray(off + 4, off + len));
|
|
1786
|
+
off += len;
|
|
1787
|
+
}
|
|
1788
|
+
return { lines, rest: off };
|
|
1789
|
+
}
|
|
1790
|
+
async function lsRefs(remote, headers = {}, { service = "git-upload-pack", prefix = null } = {}) {
|
|
1791
|
+
const r = await fetch(`${remote}/info/refs?service=${service}`, { headers });
|
|
1792
|
+
if (!r.ok) {
|
|
1793
|
+
if (r.status === 404) return {};
|
|
1794
|
+
throw new Error(`info/refs ${r.status}: ${(await r.text()).slice(0, 180)}`);
|
|
1795
|
+
}
|
|
1796
|
+
const buf = new Uint8Array(await r.arrayBuffer());
|
|
1797
|
+
const banner = readPktLines(buf, 0);
|
|
1798
|
+
const { lines } = readPktLines(buf, banner.rest);
|
|
1799
|
+
const refs = {};
|
|
1800
|
+
for (const line of lines) {
|
|
1801
|
+
let s = dec2.decode(line).replace(/\n$/, "");
|
|
1802
|
+
if (s.startsWith("#")) continue;
|
|
1803
|
+
const nul = s.indexOf("\0");
|
|
1804
|
+
if (nul >= 0) s = s.slice(0, nul);
|
|
1805
|
+
const sp = s.indexOf(" ");
|
|
1806
|
+
if (sp !== 40) continue;
|
|
1807
|
+
const oid = s.slice(0, 40), name = s.slice(41);
|
|
1808
|
+
if (!/^[0-9a-f]{40}$/.test(oid)) continue;
|
|
1809
|
+
if (prefix && !name.startsWith(prefix)) continue;
|
|
1810
|
+
refs[name] = oid;
|
|
1811
|
+
}
|
|
1812
|
+
return refs;
|
|
1813
|
+
}
|
|
1814
|
+
async function fetchPack(remote, headers = {}, { wants = [], haves = [] } = {}) {
|
|
1815
|
+
wants = wants.filter((w) => /^[0-9a-f]{40}$/.test(w) && w !== ZERO_OID);
|
|
1816
|
+
if (!wants.length) return null;
|
|
1817
|
+
const caps = "thin-pack ofs-delta agent=nomos-cloud";
|
|
1818
|
+
const parts = [];
|
|
1819
|
+
wants.forEach((w, i) => parts.push(pkt(`want ${w}${i === 0 ? " " + caps : ""}
|
|
1820
|
+
`)));
|
|
1821
|
+
parts.push(FLUSH);
|
|
1822
|
+
for (const h of haves) if (/^[0-9a-f]{40}$/.test(h) && h !== ZERO_OID) parts.push(pkt(`have ${h}
|
|
1823
|
+
`));
|
|
1824
|
+
parts.push(pkt("done\n"));
|
|
1825
|
+
const r = await fetch(`${remote}/git-upload-pack`, {
|
|
1826
|
+
method: "POST",
|
|
1827
|
+
headers: { "content-type": "application/x-git-upload-pack-request", "accept": "application/x-git-upload-pack-result", ...headers },
|
|
1828
|
+
body: concatBytes(parts)
|
|
1829
|
+
});
|
|
1830
|
+
if (!r.ok) throw new Error(`upload-pack ${r.status}: ${(await r.text()).slice(0, 180)}`);
|
|
1831
|
+
const buf = new Uint8Array(await r.arrayBuffer());
|
|
1832
|
+
const { lines, rest } = readPktLines(buf, 0);
|
|
1833
|
+
const packChunks = [];
|
|
1834
|
+
let sideband = false;
|
|
1835
|
+
for (const line of lines) {
|
|
1836
|
+
if (line.length === 0) continue;
|
|
1837
|
+
const head = dec2.decode(line.subarray(0, Math.min(line.length, 3)));
|
|
1838
|
+
if (head === "NAK" || head === "ACK" || head === "ERR" || head.startsWith("sha")) continue;
|
|
1839
|
+
const band = line[0];
|
|
1840
|
+
if (band === 1) {
|
|
1841
|
+
packChunks.push(line.subarray(1));
|
|
1842
|
+
sideband = true;
|
|
1843
|
+
} else if (band === 2) {
|
|
1844
|
+
} else if (band === 3) {
|
|
1845
|
+
throw new Error("upload-pack fatal: " + dec2.decode(line.subarray(1)).slice(0, 180));
|
|
1846
|
+
}
|
|
1847
|
+
}
|
|
1848
|
+
const pack = sideband ? concatBytes(packChunks) : buf.subarray(rest);
|
|
1849
|
+
if (pack.length < 12 || dec2.decode(pack.subarray(0, 4)) !== "PACK") {
|
|
1850
|
+
throw new Error(`upload-pack: no pack in response (${pack.length}B, sideband=${sideband})`);
|
|
1851
|
+
}
|
|
1852
|
+
return pack;
|
|
1853
|
+
}
|
|
1854
|
+
async function pushPack(remote, headers = {}, { updates = [], pack = null } = {}) {
|
|
1855
|
+
if (!updates.length) return { ok: true, noop: true };
|
|
1856
|
+
const cmds = [];
|
|
1857
|
+
updates.forEach((u, i) => {
|
|
1858
|
+
const old = u.oldOid || ZERO_OID;
|
|
1859
|
+
const neu = u.newOid || ZERO_OID;
|
|
1860
|
+
const caps = i === 0 ? "\0report-status" : "";
|
|
1861
|
+
cmds.push(pkt(`${old} ${neu} ${u.ref}${caps}
|
|
1862
|
+
`));
|
|
1863
|
+
});
|
|
1864
|
+
cmds.push(FLUSH);
|
|
1865
|
+
const body = concatBytes([...cmds, pack && pack.length ? pack : EMPTY_PACK]);
|
|
1866
|
+
const r = await fetch(`${remote}/git-receive-pack`, {
|
|
1867
|
+
method: "POST",
|
|
1868
|
+
headers: { "content-type": "application/x-git-receive-pack-request", ...headers },
|
|
1869
|
+
body
|
|
1870
|
+
});
|
|
1871
|
+
const txt = dec2.decode(new Uint8Array(await r.arrayBuffer()));
|
|
1872
|
+
const unpacked = /unpack ok/.test(txt);
|
|
1873
|
+
const everyRefOk = updates.every((u) => new RegExp(`ok ${u.ref.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}`).test(txt));
|
|
1874
|
+
if (!(r.ok && unpacked && everyRefOk && !/\bng /.test(txt))) {
|
|
1875
|
+
throw new Error(`receive-pack ${r.status}: ${txt.replace(/[^\x20-\x7e]/g, " ").trim().slice(0, 200)}`);
|
|
1876
|
+
}
|
|
1877
|
+
return { ok: true };
|
|
1878
|
+
}
|
|
1879
|
+
|
|
1880
|
+
// src/_adapter/managed-promotion.ts
|
|
1881
|
+
var RELEASE_RECEIPT_PREFIX = "nomos-release-receipt:";
|
|
1882
|
+
function prepareManagedPromotion(report, candidateHash, domain, opts = {}) {
|
|
1883
|
+
if (!report.sourceVerified || !report.complete || !report.evolve?.ok) {
|
|
1884
|
+
const reason = !report.sourceVerified ? "the source ledger did not verify" : !report.complete ? "some historical premises could not be re-derived" : "the candidate fails deterministic schema evolution";
|
|
1885
|
+
return { ok: false, stage: "preflight", report, error: `promotion refused: ${reason}` };
|
|
1886
|
+
}
|
|
1887
|
+
const acceptanceReason = opts.acceptanceReason?.trim();
|
|
1888
|
+
if (!report.promotable && !acceptanceReason) {
|
|
1889
|
+
return {
|
|
1890
|
+
ok: false,
|
|
1891
|
+
stage: "acceptance",
|
|
1892
|
+
report,
|
|
1893
|
+
error: "exact history changes under the candidate; provide an acceptanceReason to make that business decision accountable"
|
|
1894
|
+
};
|
|
1895
|
+
}
|
|
1896
|
+
if (!report.atStateRoot || !report.historyHash || !report.reportHash || !report.projection) {
|
|
1897
|
+
return {
|
|
1898
|
+
ok: false,
|
|
1899
|
+
stage: "preflight",
|
|
1900
|
+
report,
|
|
1901
|
+
error: "the complete pre-flight report is missing its state, projection, or content proof"
|
|
1902
|
+
};
|
|
1903
|
+
}
|
|
1904
|
+
const provedDomains = report.domains?.length ? report.domains : [report.domain];
|
|
1905
|
+
if (!provedDomains.includes(domain)) {
|
|
1906
|
+
return {
|
|
1907
|
+
ok: false,
|
|
1908
|
+
stage: "preflight",
|
|
1909
|
+
report,
|
|
1910
|
+
error: `the package-wide report does not cover the generated business area '${domain}'`
|
|
1911
|
+
};
|
|
1912
|
+
}
|
|
1913
|
+
const reportEvidence = JSON.parse(JSON.stringify(report));
|
|
1914
|
+
if (Array.isArray(reportEvidence.divergences)) {
|
|
1915
|
+
for (const divergence of reportEvidence.divergences) {
|
|
1916
|
+
if (divergence && typeof divergence === "object") {
|
|
1917
|
+
delete divergence.atCommit;
|
|
1918
|
+
}
|
|
1919
|
+
}
|
|
1920
|
+
}
|
|
1921
|
+
const preflight = {
|
|
1922
|
+
candidateHash,
|
|
1923
|
+
// A package may be reached through any one of its generated domain clients. The
|
|
1924
|
+
// kernel chooses one canonical domain for the private receipt so the exact same
|
|
1925
|
+
// package/history proof has one identity regardless of that entry point.
|
|
1926
|
+
domain: report.domain,
|
|
1927
|
+
atHash: report.atHash,
|
|
1928
|
+
atStateRoot: report.atStateRoot,
|
|
1929
|
+
historyHash: report.historyHash,
|
|
1930
|
+
reportHash: report.reportHash,
|
|
1931
|
+
reportJson: JSON.stringify(reportEvidence),
|
|
1932
|
+
complete: true,
|
|
1933
|
+
evolveOk: true,
|
|
1934
|
+
rawPromotable: report.promotable,
|
|
1935
|
+
projectionEqual: report.projection.equal,
|
|
1936
|
+
replayedIntents: report.replayedIntents,
|
|
1937
|
+
candidateIntents: report.candidateIntents,
|
|
1938
|
+
divergenceCount: report.divergences.length,
|
|
1939
|
+
baselineRoot: report.projection.baselineRoot,
|
|
1940
|
+
candidateRoot: report.projection.candidateRoot,
|
|
1941
|
+
...!report.promotable && acceptanceReason ? { acceptanceReason } : {}
|
|
1942
|
+
};
|
|
1943
|
+
return {
|
|
1944
|
+
ok: true,
|
|
1945
|
+
stage: "ready",
|
|
1946
|
+
report,
|
|
1947
|
+
receiptId: `${RELEASE_RECEIPT_PREFIX}${report.reportHash}`,
|
|
1948
|
+
payload: {
|
|
1949
|
+
domainHash: candidateHash,
|
|
1950
|
+
...opts.replaces ? { replaces: opts.replaces } : {},
|
|
1951
|
+
retireReplaced: opts.retireReplaced ?? false,
|
|
1952
|
+
...opts.dispositions ? { dispositions: opts.dispositions } : {},
|
|
1953
|
+
preflight
|
|
1954
|
+
}
|
|
1955
|
+
};
|
|
1956
|
+
}
|
|
1957
|
+
function prepareManagedRestoration(report, currentHash, priorHash, domain, opts = {}) {
|
|
1958
|
+
if (!report.dispositions || typeof report.dispositions !== "object") {
|
|
1959
|
+
return {
|
|
1960
|
+
ok: false,
|
|
1961
|
+
stage: "preflight",
|
|
1962
|
+
report,
|
|
1963
|
+
error: "restoration refused: the exact-history proof did not derive its predecessor dispositions"
|
|
1964
|
+
};
|
|
1965
|
+
}
|
|
1966
|
+
const acceptanceReason = opts.acceptanceReason?.trim();
|
|
1967
|
+
const prepared = prepareManagedPromotion(report, priorHash, domain, {
|
|
1968
|
+
dispositions: report.dispositions,
|
|
1969
|
+
...acceptanceReason ? { acceptanceReason } : {}
|
|
1970
|
+
});
|
|
1971
|
+
if (!prepared.ok) {
|
|
1972
|
+
return {
|
|
1973
|
+
...prepared,
|
|
1974
|
+
stage: prepared.stage === "acceptance" ? "acceptance" : "preflight"
|
|
1975
|
+
};
|
|
1976
|
+
}
|
|
1977
|
+
if (!acceptanceReason) {
|
|
1978
|
+
return {
|
|
1979
|
+
ok: false,
|
|
1980
|
+
stage: "acceptance",
|
|
1981
|
+
report,
|
|
1982
|
+
error: "restoring previous law needs a business reason even when historical outcomes are unchanged"
|
|
1983
|
+
};
|
|
1984
|
+
}
|
|
1985
|
+
return {
|
|
1986
|
+
ok: true,
|
|
1987
|
+
stage: "ready",
|
|
1988
|
+
report,
|
|
1989
|
+
receiptId: prepared.receiptId,
|
|
1990
|
+
payload: {
|
|
1991
|
+
candidateHash: currentHash,
|
|
1992
|
+
priorHash,
|
|
1993
|
+
dispositions: report.dispositions,
|
|
1994
|
+
preflight: prepared.payload.preflight
|
|
1995
|
+
}
|
|
1996
|
+
};
|
|
1997
|
+
}
|
|
1998
|
+
|
|
1999
|
+
// src/_adapter/engine.ts
|
|
2000
|
+
var enc3 = new TextEncoder();
|
|
2001
|
+
var dec3 = new TextDecoder();
|
|
2002
|
+
var BRANCH = "main";
|
|
2003
|
+
var RESULT_PACK_INLINE_MAX = 256 * 1024;
|
|
2004
|
+
var dirOf = (n) => n;
|
|
2005
|
+
var repoArgOf = (ws) => `/work/ws/${ws}`;
|
|
2006
|
+
var gitdirOf = (ws) => `/work/ws/${ws}/nomos.git`;
|
|
2007
|
+
function b64Bytes(bytes) {
|
|
2008
|
+
let s = "";
|
|
2009
|
+
for (let i = 0; i < bytes.length; i += 32768) {
|
|
2010
|
+
s += String.fromCharCode(...bytes.subarray(i, i + 32768));
|
|
2011
|
+
}
|
|
2012
|
+
return btoa(s);
|
|
2013
|
+
}
|
|
2014
|
+
function b64Json(o) {
|
|
2015
|
+
return b64Bytes(enc3.encode(JSON.stringify(o)));
|
|
2016
|
+
}
|
|
2017
|
+
var unpack = (p) => {
|
|
2018
|
+
const v = typeof p === "bigint" ? p : BigInt(p);
|
|
2019
|
+
return { ptr: Number(v >> 32n), len: Number(v & 0xffffffffn) };
|
|
2020
|
+
};
|
|
2021
|
+
async function sha256hex(t) {
|
|
2022
|
+
const b = await crypto.subtle.digest("SHA-256", enc3.encode(t));
|
|
2023
|
+
return [...new Uint8Array(b)].map((x) => x.toString(16).padStart(2, "0")).join("");
|
|
2024
|
+
}
|
|
2025
|
+
function osEntropyBuffer(n) {
|
|
2026
|
+
const bytes = new Uint8Array(n * 8);
|
|
2027
|
+
for (let o = 0; o < bytes.length; o += 65536) crypto.getRandomValues(bytes.subarray(o, Math.min(o + 65536, bytes.length)));
|
|
2028
|
+
const out = new Array(n), T = 2 ** 53;
|
|
2029
|
+
for (let i = 0; i < n; i++) {
|
|
2030
|
+
let z = 0n;
|
|
2031
|
+
for (let b = 7; b >= 0; b--) z = z << 8n | BigInt(bytes[i * 8 + b]);
|
|
2032
|
+
out[i] = Number(z >> 11n) / T;
|
|
2033
|
+
}
|
|
2034
|
+
return out;
|
|
2035
|
+
}
|
|
2036
|
+
function stringifyBig(o) {
|
|
2037
|
+
return JSON.stringify(o, (_k, v) => typeof v === "bigint" ? `@@B:${v}@@` : v).replace(/"@@B:(\d+)@@"/g, "$1");
|
|
2038
|
+
}
|
|
2039
|
+
var log = (evt, fields = {}) => console.log(JSON.stringify({ evt, t: Date.now(), ...fields }));
|
|
2040
|
+
function call(ex, mode, fields, STDERR) {
|
|
2041
|
+
const req = enc3.encode(JSON.stringify({ mode, ...fields }));
|
|
2042
|
+
const reqPtr = ex.git_holon_alloc(req.length);
|
|
2043
|
+
try {
|
|
2044
|
+
new Uint8Array(ex.memory.buffer, reqPtr, req.length).set(req);
|
|
2045
|
+
let packed;
|
|
2046
|
+
try {
|
|
2047
|
+
packed = ex.git_holon_call(reqPtr, req.length);
|
|
2048
|
+
} catch (e) {
|
|
2049
|
+
const tail = STDERR.length ? ` | stderr: ${STDERR.slice(-8).join(" / ")}` : "";
|
|
2050
|
+
throw new Error(`${String(e?.stack || e)}${tail}`);
|
|
2051
|
+
}
|
|
2052
|
+
const { ptr, len } = unpack(packed);
|
|
2053
|
+
try {
|
|
2054
|
+
const e = JSON.parse(dec3.decode(new Uint8Array(ex.memory.buffer, ptr, len).slice()));
|
|
2055
|
+
if (!e.ok) throw new Error((e.error || "holon error") + (STDERR.length ? ` | ${STDERR.slice(-4).join(" / ")}` : ""));
|
|
2056
|
+
return e.result;
|
|
2057
|
+
} finally {
|
|
2058
|
+
ex.git_holon_dealloc(ptr, len);
|
|
2059
|
+
}
|
|
2060
|
+
} finally {
|
|
2061
|
+
ex.git_holon_dealloc(reqPtr, req.length);
|
|
2062
|
+
}
|
|
2063
|
+
}
|
|
2064
|
+
var repoArgOfGitdir = (gitdir) => gitdir.replace(/\/nomos\.git$/, "");
|
|
2065
|
+
var bytesFromB64 = (b64) => Uint8Array.from(atob(b64), (c) => c.charCodeAt(0));
|
|
2066
|
+
var kgit = {
|
|
2067
|
+
// resolveRef → tip oid hex, or null if the ref is absent (replaces git.resolveRef(...).catch(()=>null)).
|
|
2068
|
+
resolveRef: (eng2, gitdir, ref) => JSON.parse(call(eng2.ex, "resolve_ref", { repoArg: repoArgOfGitdir(gitdir), ref }, eng2.STDERR)).oid,
|
|
2069
|
+
// readBlob → the blob bytes at `path` inside commit `oid`, or null if not found (no throw).
|
|
2070
|
+
readBlob: (eng2, gitdir, oid, path) => {
|
|
2071
|
+
const r = JSON.parse(call(eng2.ex, "read_blob", { repoArg: repoArgOfGitdir(gitdir), oid, path }, eng2.STDERR));
|
|
2072
|
+
return r.ok ? bytesFromB64(r.blobB64) : null;
|
|
2073
|
+
},
|
|
2074
|
+
// intentsAbove → [{ oid, intentB64 }] oldest→newest over (base, head] (replaces log + readBlob intent.json).
|
|
2075
|
+
// `limit` (0 = all) caps to the most-recent N intent commits (replaces git.log depth).
|
|
2076
|
+
intentsAbove: (eng2, gitdir, head, base = "", limit = 0) => JSON.parse(call(eng2.ex, "intents_above", { repoArg: repoArgOfGitdir(gitdir), head, base, limit }, eng2.STDERR)).intents,
|
|
2077
|
+
// listRefs → [{ ref, oid }] under prefix (replaces git.listRefs for LOCAL refs).
|
|
2078
|
+
listRefs: (eng2, gitdir, prefix) => JSON.parse(call(eng2.ex, "list_refs", { repoArg: repoArgOfGitdir(gitdir), prefix }, eng2.STDERR)).refs,
|
|
2079
|
+
// writeRef → force-point a ref (plain oid or `ref:`-symbolic) (replaces git.writeRef).
|
|
2080
|
+
writeRef: (eng2, gitdir, ref, value) => JSON.parse(call(eng2.ex, "write_ref", { repoArg: repoArgOfGitdir(gitdir), ref, value }, eng2.STDERR)),
|
|
2081
|
+
// deleteRef → remove a local ref, idempotent (replaces git.deleteRef).
|
|
2082
|
+
deleteRef: (eng2, gitdir, ref) => JSON.parse(call(eng2.ex, "delete_ref", { repoArg: repoArgOfGitdir(gitdir), ref }, eng2.STDERR)),
|
|
2083
|
+
// applyPack → ingest a pack's objects into ws's repo (kernel apply_pack; refs are the caller's job).
|
|
2084
|
+
applyPack: (eng2, ws, packBytes) => applyPackInto(eng2, ws, packBytes),
|
|
2085
|
+
// writeMetaCommit → blobs→tree→commit→ref for a CUSTODY-META fact (checkpoint/refused/outbox/shape).
|
|
2086
|
+
// `files`: [{ path, bytes:Uint8Array }]; returns the commit oid (replaces writeBlob+writeTree+writeCommit[+writeRef]).
|
|
2087
|
+
writeMetaCommit: (eng2, gitdir, { parents = [], files, message = "nomos custody meta", timeSecs = 0, ref = "" }) => JSON.parse(call(eng2.ex, "write_meta_commit", {
|
|
2088
|
+
repoArg: repoArgOfGitdir(gitdir),
|
|
2089
|
+
parents,
|
|
2090
|
+
files: files.map((f) => ({ path: f.path, blobB64: b64Bytes(f.bytes) })),
|
|
2091
|
+
message,
|
|
2092
|
+
timeSecs,
|
|
2093
|
+
ref
|
|
2094
|
+
}, eng2.STDERR)).oid
|
|
2095
|
+
};
|
|
2096
|
+
var installPayload = (hash, usda, installedBy, dispositions) => ({ domainHash: hash, packageUsda: usda, installedBy, dependencies: [], finalizers: [], ...dispositions ? { dispositions } : {} });
|
|
2097
|
+
var stagePayload = (hash, usda, installedBy, supersedes) => ({
|
|
2098
|
+
domainHash: hash,
|
|
2099
|
+
packageUsda: usda,
|
|
2100
|
+
installedBy,
|
|
2101
|
+
dependencies: [],
|
|
2102
|
+
...supersedes ? { supersedes } : {},
|
|
2103
|
+
finalizers: []
|
|
2104
|
+
});
|
|
2105
|
+
async function createEngine({ wasmModule: wasmModule2, bootstrapPkg, nomosPkg, replica: replica2, installedBy = "nomos-cloud" }) {
|
|
2106
|
+
const hashes = { bootstrap: await sha256hex(bootstrapPkg), nomos: await sha256hex(nomosPkg) };
|
|
2107
|
+
const root = /* @__PURE__ */ new Map();
|
|
2108
|
+
root.set("domain.package.usda", new File(enc3.encode(bootstrapPkg)));
|
|
2109
|
+
root.set("ws", new Directory(/* @__PURE__ */ new Map()));
|
|
2110
|
+
const preopen = new PreopenDirectory("/work", root);
|
|
2111
|
+
const STDERR = [];
|
|
2112
|
+
const pushStderr = (l) => {
|
|
2113
|
+
if (STDERR.length >= 500) STDERR.splice(0, STDERR.length - 499);
|
|
2114
|
+
STDERR.push(l);
|
|
2115
|
+
};
|
|
2116
|
+
const fds = [new OpenFile(new File([])), ConsoleStdout.lineBuffered(() => {
|
|
2117
|
+
}), ConsoleStdout.lineBuffered(pushStderr), preopen];
|
|
2118
|
+
const wasi = new WASI(["wasm_git_holon", "reactor"], [], fds, { debug: false });
|
|
2119
|
+
const inst = await WebAssembly.instantiate(wasmModule2, { wasi_snapshot_preview1: wasi.wasiImport });
|
|
2120
|
+
const code = wasi.start(inst);
|
|
2121
|
+
if (code !== 0) throw new Error(`reactor init exited ${code}; stderr=${STDERR.join("\n")}`);
|
|
2122
|
+
const instExports = inst.exports;
|
|
2123
|
+
for (const n of ["memory", "git_holon_alloc", "git_holon_dealloc", "git_holon_call"]) if (!instExports[n]) throw new Error(`missing export ${n}`);
|
|
2124
|
+
const eng2 = {
|
|
2125
|
+
ex: instExports,
|
|
2126
|
+
preopen,
|
|
2127
|
+
STDERR,
|
|
2128
|
+
seq: 0,
|
|
2129
|
+
replica: replica2,
|
|
2130
|
+
hashes,
|
|
2131
|
+
bootstrapPkg,
|
|
2132
|
+
nomosPkg,
|
|
2133
|
+
installedBy,
|
|
2134
|
+
fs: makeGitFs(preopen, "/work", { File, Directory }),
|
|
2135
|
+
mounted: /* @__PURE__ */ new Map(),
|
|
2136
|
+
// ws -> { restored, remoteMain, restoreError }
|
|
2137
|
+
mainIntents: /* @__PURE__ */ new Map()
|
|
2138
|
+
// ws -> { head, ids:Set, oids:Set } (admission idempotence)
|
|
2139
|
+
};
|
|
2140
|
+
return eng2;
|
|
2141
|
+
}
|
|
2142
|
+
function directiveRoutes(eng2) {
|
|
2143
|
+
return /* @__PURE__ */ new Map();
|
|
2144
|
+
}
|
|
2145
|
+
var routeTagHexOfHomeKey = async (homeKey) => (await sha256hex(`nomos-route:${homeKey}`)).slice(0, 12);
|
|
2146
|
+
function routeTagHexOfMintedId(id) {
|
|
2147
|
+
const i = id.indexOf("_");
|
|
2148
|
+
if (i <= 0) return void 0;
|
|
2149
|
+
const body = id.slice(i + 1).replace(/-/g, "").toLowerCase();
|
|
2150
|
+
return /^[0-9a-f]{32}$/.test(body) ? body.slice(0, 12) : void 0;
|
|
2151
|
+
}
|
|
2152
|
+
async function wrongHomeVerdict(shardRouting, route, payload) {
|
|
2153
|
+
const value = payload ? payload[route.key] : void 0;
|
|
2154
|
+
if (typeof value !== "string" || !value) return null;
|
|
2155
|
+
const rows = await shardRouting.assignments() || [];
|
|
2156
|
+
let assignment = null;
|
|
2157
|
+
if (route.via === "axis") {
|
|
2158
|
+
assignment = rows.find((a) => a.homeKey === value) || null;
|
|
2159
|
+
} else {
|
|
2160
|
+
const tag = routeTagHexOfMintedId(value);
|
|
2161
|
+
if (tag !== void 0) {
|
|
2162
|
+
for (const a of rows) {
|
|
2163
|
+
if (await routeTagHexOfHomeKey(String(a.homeKey)) === tag) {
|
|
2164
|
+
assignment = a;
|
|
2165
|
+
break;
|
|
2166
|
+
}
|
|
2167
|
+
}
|
|
2168
|
+
}
|
|
2169
|
+
}
|
|
2170
|
+
if (!assignment) {
|
|
2171
|
+
return {
|
|
2172
|
+
error: `no shard placement for this write's home ('${route.key}': ${value.slice(0, 80)}) \u2014 author the derived placement intent (birth<Type>) on the coordinator '${shardRouting.coordinatorWs}' first, then POST /dead-letters/retry`,
|
|
2173
|
+
deadLettered: true
|
|
2174
|
+
};
|
|
2175
|
+
}
|
|
2176
|
+
const movingTo = typeof assignment.status === "string" && assignment.status.startsWith("moving:") ? assignment.status.slice("moving:".length) : null;
|
|
2177
|
+
if (movingTo) {
|
|
2178
|
+
return {
|
|
2179
|
+
error: `home-moving: home '${value.slice(0, 80)}' is migrating ${assignment.shard} -> ${movingTo} (shard map v${assignment.mapVersion ?? 1}) \u2014 parked; a routed client retries automatically after the handoff seals`,
|
|
2180
|
+
homeMoving: { homeKey: value, from: assignment.shard, target: movingTo, workspace: `${shardRouting.coordinatorWs}--${movingTo}` },
|
|
2181
|
+
deadLettered: true
|
|
2182
|
+
};
|
|
2183
|
+
}
|
|
2184
|
+
if (shardRouting.selfLabel === assignment.shard) return null;
|
|
2185
|
+
const correct = `${shardRouting.coordinatorWs}--${assignment.shard}`;
|
|
2186
|
+
return {
|
|
2187
|
+
error: `wrong-home: this write's home is assigned to '${correct}' (shard map v${assignment.mapVersion ?? 1}) \u2014 the client's shard map is stale; a routed client re-routes and retries automatically`,
|
|
2188
|
+
wrongHome: { workspace: correct, ...assignment.mapVersion !== void 0 ? { mapVersion: assignment.mapVersion } : {} }
|
|
2189
|
+
};
|
|
2190
|
+
}
|
|
2191
|
+
var SUMMARY_SEP = "";
|
|
2192
|
+
var summaryKey = (readId, group) => `${readId}${SUMMARY_SEP}${group}`;
|
|
2193
|
+
var splitSummaryKey = (key) => {
|
|
2194
|
+
const i = key.indexOf(SUMMARY_SEP);
|
|
2195
|
+
return [key.slice(0, i), key.slice(i + 1)];
|
|
2196
|
+
};
|
|
2197
|
+
var NOMOS_SHARD_ROWS_READ = "nomosShardRows";
|
|
2198
|
+
function tenantTypes(eng2) {
|
|
2199
|
+
return /* @__PURE__ */ new Set();
|
|
2200
|
+
}
|
|
2201
|
+
function placementDirectives(eng2) {
|
|
2202
|
+
return /* @__PURE__ */ new Map();
|
|
2203
|
+
}
|
|
2204
|
+
function scopedReads(eng2) {
|
|
2205
|
+
return [];
|
|
2206
|
+
}
|
|
2207
|
+
function evalCanonicalPred(pred, get) {
|
|
2208
|
+
if (!pred || typeof pred !== "object") return true;
|
|
2209
|
+
if (pred.kind === "and") return (pred.clauses || []).every((c) => evalCanonicalPred(c, get));
|
|
2210
|
+
if (pred.kind === "or") return (pred.clauses || []).some((c) => evalCanonicalPred(c, get));
|
|
2211
|
+
const raw = get(pred.field);
|
|
2212
|
+
const present = raw !== void 0 && raw !== null;
|
|
2213
|
+
const canonical = present ? String(raw) : void 0;
|
|
2214
|
+
if (pred.kind === "eq") return present && canonical === pred.value;
|
|
2215
|
+
if (pred.kind === "ne") return !present || canonical !== pred.value;
|
|
2216
|
+
return true;
|
|
2217
|
+
}
|
|
2218
|
+
function globalAggregates(eng2) {
|
|
2219
|
+
return /* @__PURE__ */ new Set();
|
|
2220
|
+
}
|
|
2221
|
+
function parseIntentEvents(blob) {
|
|
2222
|
+
let doc;
|
|
2223
|
+
try {
|
|
2224
|
+
doc = JSON.parse(dec3.decode(blob));
|
|
2225
|
+
} catch {
|
|
2226
|
+
return [];
|
|
2227
|
+
}
|
|
2228
|
+
const events = Array.isArray(doc && doc.events) ? doc.events : [];
|
|
2229
|
+
return events.map((ev) => {
|
|
2230
|
+
const sets = /* @__PURE__ */ new Map();
|
|
2231
|
+
for (const op of ev.ops || []) {
|
|
2232
|
+
const v = op && op.op && op.op.Set;
|
|
2233
|
+
if (v === void 0) continue;
|
|
2234
|
+
if (typeof v.Str === "string") sets.set(op.field, v.Str);
|
|
2235
|
+
else if (typeof v.Int === "number") sets.set(op.field, v.Int);
|
|
2236
|
+
}
|
|
2237
|
+
return { aggregate: String(ev.aggregate || ""), sets };
|
|
2238
|
+
});
|
|
2239
|
+
}
|
|
2240
|
+
var groupOf = (read, data) => {
|
|
2241
|
+
if (read.by === null) return "";
|
|
2242
|
+
const v = data ? data[read.by] : void 0;
|
|
2243
|
+
return v === void 0 || v === null ? void 0 : String(v);
|
|
2244
|
+
};
|
|
2245
|
+
var tallyOf = (eng2, ws, read, group) => read.kind === "count" ? count(eng2, ws, read.id, group).count : read.kind === "sum" ? sum(eng2, ws, read.id, group).sum : extremum(eng2, ws, read.id, read.kind, group).value;
|
|
2246
|
+
var isExtremumRead = (read) => read.kind === "min" || read.kind === "max";
|
|
2247
|
+
var extremumMonotone = (read, prev, next) => next === null && prev === null || next !== null && (prev === null || (read.kind === "min" ? next <= prev : next >= prev));
|
|
2248
|
+
function beginSummaryCapture(eng2, ws, ctx, blob) {
|
|
2249
|
+
const events = parseIntentEvents(blob);
|
|
2250
|
+
if (events.length === 0) return null;
|
|
2251
|
+
const pre = /* @__PURE__ */ new Map();
|
|
2252
|
+
const candidates = /* @__PURE__ */ new Set();
|
|
2253
|
+
for (const ev of events) {
|
|
2254
|
+
if (!pre.has(ev.aggregate)) {
|
|
2255
|
+
const rows = qById(eng2, ws, ev.aggregate);
|
|
2256
|
+
pre.set(ev.aggregate, rows.length ? { type: rows[0].type, data: rows[0].data || {} } : null);
|
|
2257
|
+
}
|
|
2258
|
+
const before = pre.get(ev.aggregate);
|
|
2259
|
+
const evType = ev.sets.get("__type") ?? (before ? before.type : void 0);
|
|
2260
|
+
for (const read of ctx.reads) {
|
|
2261
|
+
if (read.of !== evType) continue;
|
|
2262
|
+
const oldGroup = before ? groupOf(read, before.data) : void 0;
|
|
2263
|
+
if (oldGroup !== void 0) candidates.add(summaryKey(read.id, oldGroup));
|
|
2264
|
+
if (read.by === null) candidates.add(summaryKey(read.id, ""));
|
|
2265
|
+
else if (ev.sets.has(read.by)) candidates.add(summaryKey(read.id, String(ev.sets.get(read.by))));
|
|
2266
|
+
}
|
|
2267
|
+
}
|
|
2268
|
+
const preTallies = /* @__PURE__ */ new Map();
|
|
2269
|
+
for (const key of candidates) {
|
|
2270
|
+
const [readId, group] = splitSummaryKey(key);
|
|
2271
|
+
const read = ctx.reads.find((r) => r.id === readId);
|
|
2272
|
+
const v = tallyOf(eng2, ws, read, group);
|
|
2273
|
+
preTallies.set(key, v);
|
|
2274
|
+
if (!ctx.prevs.has(key)) ctx.prevs.set(key, { read, group, value: v });
|
|
2275
|
+
}
|
|
2276
|
+
return { events, pre, candidates, preTallies };
|
|
2277
|
+
}
|
|
2278
|
+
function finishSummaryCapture(eng2, ws, ctx, cap, oid, intentId) {
|
|
2279
|
+
const deltas = [];
|
|
2280
|
+
for (const key of cap.candidates) {
|
|
2281
|
+
const [readId, group] = splitSummaryKey(key);
|
|
2282
|
+
const read = ctx.reads.find((r) => r.id === readId);
|
|
2283
|
+
const after = tallyOf(eng2, ws, read, group);
|
|
2284
|
+
if (isExtremumRead(read)) {
|
|
2285
|
+
const before = cap.preTallies.has(key) ? cap.preTallies.get(key) : null;
|
|
2286
|
+
if (after !== before) {
|
|
2287
|
+
deltas.push({ readId, group, delta: after });
|
|
2288
|
+
if (!extremumMonotone(read, before, after)) ctx.rederived.add(key);
|
|
2289
|
+
}
|
|
2290
|
+
} else {
|
|
2291
|
+
const before = cap.preTallies.get(key) ?? 0;
|
|
2292
|
+
if (after !== before) deltas.push({ readId, group, delta: after - before });
|
|
2293
|
+
}
|
|
2294
|
+
ctx.values.set(key, { read, group, value: after });
|
|
2295
|
+
}
|
|
2296
|
+
let rowsDelta = 0;
|
|
2297
|
+
for (const [aggregateId, before] of cap.pre) {
|
|
2298
|
+
const rows = qById(eng2, ws, aggregateId);
|
|
2299
|
+
const type = rows.length ? rows[0].type : before ? before.type : void 0;
|
|
2300
|
+
if (type === void 0 || !tenantTypes(eng2).has(type)) continue;
|
|
2301
|
+
const existsAfter = rows.length > 0;
|
|
2302
|
+
const flip = !before && existsAfter ? 1 : before && !existsAfter ? -1 : 0;
|
|
2303
|
+
if (flip === 0) continue;
|
|
2304
|
+
rowsDelta += flip;
|
|
2305
|
+
const homeKey = ctx.homeKeySet && ctx.homeKeySet.has(aggregateId) ? aggregateId : ctx.homeTags ? ctx.homeTags.get(routeTagHexOfMintedId(aggregateId) ?? "") : void 0;
|
|
2306
|
+
if (homeKey !== void 0) {
|
|
2307
|
+
deltas.push({ readId: NOMOS_SHARD_ROWS_READ, group: homeKey, delta: flip });
|
|
2308
|
+
ctx.rowsByHome.set(homeKey, (ctx.rowsByHome.get(homeKey) ?? 0) + flip);
|
|
2309
|
+
}
|
|
2310
|
+
}
|
|
2311
|
+
if (rowsDelta !== 0) {
|
|
2312
|
+
deltas.push({ readId: NOMOS_SHARD_ROWS_READ, group: "", delta: rowsDelta });
|
|
2313
|
+
ctx.rowsDelta += rowsDelta;
|
|
2314
|
+
}
|
|
2315
|
+
if (deltas.length > 0) ctx.events.push({ oid, intentId: intentId || "", deltas });
|
|
2316
|
+
}
|
|
2317
|
+
function summaryOfCapture(ctx, fromOid, toOid) {
|
|
2318
|
+
if (ctx.events.length === 0 || !toOid || fromOid === toOid) return null;
|
|
2319
|
+
const touched = /* @__PURE__ */ new Set();
|
|
2320
|
+
for (const ev of ctx.events) for (const d of ev.deltas) touched.add(summaryKey(d.readId, d.group));
|
|
2321
|
+
const subtotals = [];
|
|
2322
|
+
for (const [key, cur] of ctx.values) {
|
|
2323
|
+
const extremum2 = isExtremumRead(cur.read);
|
|
2324
|
+
const prev = ctx.prevs.get(key);
|
|
2325
|
+
const prevValue = prev ? prev.value : extremum2 ? null : 0;
|
|
2326
|
+
if (cur.value === prevValue && !touched.has(key)) continue;
|
|
2327
|
+
subtotals.push({
|
|
2328
|
+
readId: cur.read.id,
|
|
2329
|
+
group: cur.group,
|
|
2330
|
+
kind: cur.read.kind,
|
|
2331
|
+
prev: prevValue,
|
|
2332
|
+
value: cur.value,
|
|
2333
|
+
...extremum2 && ctx.rederived.has(key) ? { rederived: true } : {}
|
|
2334
|
+
});
|
|
2335
|
+
}
|
|
2336
|
+
if (subtotals.length === 0 && ctx.rowsDelta === 0 && ctx.rowsByHome.size === 0) return null;
|
|
2337
|
+
return {
|
|
2338
|
+
fromOid: fromOid || "",
|
|
2339
|
+
toOid,
|
|
2340
|
+
subtotals: subtotals.sort((a, b) => summaryKey(a.readId, a.group) < summaryKey(b.readId, b.group) ? -1 : 1),
|
|
2341
|
+
events: ctx.events,
|
|
2342
|
+
rowsDelta: ctx.rowsDelta,
|
|
2343
|
+
// SLICE 8: the exact per-home row deltas — the adapter closes each against the
|
|
2344
|
+
// coordinator-held per-home subtotal exactly like the "" grand total.
|
|
2345
|
+
rowsByHome: Object.fromEntries([...ctx.rowsByHome.entries()].sort((a, b) => a[0] < b[0] ? -1 : 1))
|
|
2346
|
+
};
|
|
2347
|
+
}
|
|
2348
|
+
async function deriveSummarySuffix(eng2, ws, fromOid) {
|
|
2349
|
+
const reads = scopedReads(eng2);
|
|
2350
|
+
const gitdir = gitdirOf(ws);
|
|
2351
|
+
const head = kgit.resolveRef(eng2, gitdir, BRANCH);
|
|
2352
|
+
if (!head) return { ok: false, error: "no local main to derive from" };
|
|
2353
|
+
const commits = kgit.intentsAbove(eng2, gitdir, head);
|
|
2354
|
+
const state = /* @__PURE__ */ new Map();
|
|
2355
|
+
const running = /* @__PURE__ */ new Map();
|
|
2356
|
+
const bump = (key, d) => running.set(key, (running.get(key) ?? 0) + d);
|
|
2357
|
+
const extRows = /* @__PURE__ */ new Map();
|
|
2358
|
+
const extRunning = /* @__PURE__ */ new Map();
|
|
2359
|
+
const extRederived = /* @__PURE__ */ new Set();
|
|
2360
|
+
const extBucketRows = (key) => {
|
|
2361
|
+
let m = extRows.get(key);
|
|
2362
|
+
if (!m) {
|
|
2363
|
+
m = /* @__PURE__ */ new Map();
|
|
2364
|
+
extRows.set(key, m);
|
|
2365
|
+
}
|
|
2366
|
+
return m;
|
|
2367
|
+
};
|
|
2368
|
+
const recomputeExt = (read, key) => {
|
|
2369
|
+
let v = null;
|
|
2370
|
+
const m = extRows.get(key);
|
|
2371
|
+
if (m) for (const x of m.values()) v = v === null ? x : read.kind === "min" ? Math.min(v, x) : Math.max(v, x);
|
|
2372
|
+
return v;
|
|
2373
|
+
};
|
|
2374
|
+
const homeKeySet = /* @__PURE__ */ new Set();
|
|
2375
|
+
const homeTags = /* @__PURE__ */ new Map();
|
|
2376
|
+
let prevs = null;
|
|
2377
|
+
let extPrevs = null;
|
|
2378
|
+
let events = [];
|
|
2379
|
+
let inSuffix = fromOid === "" || fromOid === void 0;
|
|
2380
|
+
if (inSuffix) {
|
|
2381
|
+
prevs = /* @__PURE__ */ new Map();
|
|
2382
|
+
extPrevs = /* @__PURE__ */ new Map();
|
|
2383
|
+
}
|
|
2384
|
+
let sawFrom = inSuffix;
|
|
2385
|
+
for (const c of commits) {
|
|
2386
|
+
let blob = null;
|
|
2387
|
+
try {
|
|
2388
|
+
blob = bytesFromB64(c.intentB64);
|
|
2389
|
+
} catch {
|
|
2390
|
+
}
|
|
2391
|
+
const deltas = [];
|
|
2392
|
+
let intentId = "";
|
|
2393
|
+
if (blob) {
|
|
2394
|
+
let doc = null;
|
|
2395
|
+
try {
|
|
2396
|
+
doc = JSON.parse(dec3.decode(blob));
|
|
2397
|
+
} catch {
|
|
2398
|
+
}
|
|
2399
|
+
if (doc && Array.isArray(doc.strikes) && doc.strikes.length > 0) {
|
|
2400
|
+
return { ok: true, strikeBearing: true, head };
|
|
2401
|
+
}
|
|
2402
|
+
intentId = doc && doc.id || "";
|
|
2403
|
+
for (const ev of parseIntentEvents(blob)) {
|
|
2404
|
+
const cur = state.get(ev.aggregate) ?? { type: void 0, fields: /* @__PURE__ */ new Map(), exists: false };
|
|
2405
|
+
const newType = ev.sets.get("__type");
|
|
2406
|
+
if (newType !== void 0) cur.type = newType;
|
|
2407
|
+
const tallyBefore = /* @__PURE__ */ new Map();
|
|
2408
|
+
for (const read of reads) {
|
|
2409
|
+
if (read.of !== cur.type && read.of !== newType) continue;
|
|
2410
|
+
tallyBefore.set(read.id, contribution(read, cur));
|
|
2411
|
+
}
|
|
2412
|
+
const existedBefore = cur.exists;
|
|
2413
|
+
for (const [f, v] of ev.sets) cur.fields.set(f, v);
|
|
2414
|
+
cur.exists = true;
|
|
2415
|
+
state.set(ev.aggregate, cur);
|
|
2416
|
+
const effType = cur.type ?? newType;
|
|
2417
|
+
if (effType === "NomosHomeReceipt") {
|
|
2418
|
+
const hk = cur.fields.get("homeKey");
|
|
2419
|
+
if (typeof hk === "string" && hk && !homeKeySet.has(hk)) {
|
|
2420
|
+
homeKeySet.add(hk);
|
|
2421
|
+
homeTags.set(await routeTagHexOfHomeKey(hk), hk);
|
|
2422
|
+
}
|
|
2423
|
+
}
|
|
2424
|
+
if (!existedBefore && effType !== void 0 && tenantTypes(eng2).has(effType)) {
|
|
2425
|
+
bump(summaryKey(NOMOS_SHARD_ROWS_READ, ""), 1);
|
|
2426
|
+
deltas.push({ readId: NOMOS_SHARD_ROWS_READ, group: "", delta: 1 });
|
|
2427
|
+
const homeKey = homeKeySet.has(ev.aggregate) ? ev.aggregate : homeTags.get(routeTagHexOfMintedId(ev.aggregate) ?? "");
|
|
2428
|
+
if (homeKey !== void 0) {
|
|
2429
|
+
bump(summaryKey(NOMOS_SHARD_ROWS_READ, homeKey), 1);
|
|
2430
|
+
deltas.push({ readId: NOMOS_SHARD_ROWS_READ, group: homeKey, delta: 1 });
|
|
2431
|
+
}
|
|
2432
|
+
}
|
|
2433
|
+
for (const read of reads) {
|
|
2434
|
+
if (read.of !== cur.type) continue;
|
|
2435
|
+
const before = tallyBefore.get(read.id) ?? null;
|
|
2436
|
+
const after = contribution(read, cur);
|
|
2437
|
+
if (isExtremumRead(read)) {
|
|
2438
|
+
const touchedBuckets = /* @__PURE__ */ new Set();
|
|
2439
|
+
if (before) {
|
|
2440
|
+
const bk = summaryKey(read.id, before.group);
|
|
2441
|
+
if (!after || after.group !== before.group) extBucketRows(bk).delete(ev.aggregate);
|
|
2442
|
+
touchedBuckets.add(bk);
|
|
2443
|
+
}
|
|
2444
|
+
if (after) {
|
|
2445
|
+
const ak = summaryKey(read.id, after.group);
|
|
2446
|
+
extBucketRows(ak).set(ev.aggregate, after.value);
|
|
2447
|
+
touchedBuckets.add(ak);
|
|
2448
|
+
}
|
|
2449
|
+
for (const bk of touchedBuckets) {
|
|
2450
|
+
const ov = extRunning.has(bk) ? extRunning.get(bk) ?? null : null;
|
|
2451
|
+
const nv = recomputeExt(read, bk);
|
|
2452
|
+
if (nv === ov) continue;
|
|
2453
|
+
extRunning.set(bk, nv);
|
|
2454
|
+
if (inSuffix) {
|
|
2455
|
+
if (!extremumMonotone(read, ov, nv)) extRederived.add(bk);
|
|
2456
|
+
const [, group] = splitSummaryKey(bk);
|
|
2457
|
+
deltas.push({ readId: read.id, group, delta: nv, __extremum: true });
|
|
2458
|
+
}
|
|
2459
|
+
}
|
|
2460
|
+
} else {
|
|
2461
|
+
for (const d of contributionDiff(read, before, after)) {
|
|
2462
|
+
bump(summaryKey(d.readId, d.group), d.delta ?? 0);
|
|
2463
|
+
deltas.push(d);
|
|
2464
|
+
}
|
|
2465
|
+
}
|
|
2466
|
+
}
|
|
2467
|
+
}
|
|
2468
|
+
}
|
|
2469
|
+
if (inSuffix && deltas.length) {
|
|
2470
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
2471
|
+
const extLast = /* @__PURE__ */ new Map();
|
|
2472
|
+
for (const d of deltas) {
|
|
2473
|
+
const k = summaryKey(d.readId, d.group);
|
|
2474
|
+
if (d.__extremum) extLast.set(k, d.delta);
|
|
2475
|
+
else byKey.set(k, (byKey.get(k) ?? 0) + d.delta);
|
|
2476
|
+
}
|
|
2477
|
+
const merged = [...byKey.entries()].filter(([, d]) => d !== 0).map(([k, d]) => {
|
|
2478
|
+
const [readId, group] = splitSummaryKey(k);
|
|
2479
|
+
return { readId, group, delta: d };
|
|
2480
|
+
});
|
|
2481
|
+
for (const [k, v] of extLast) {
|
|
2482
|
+
const [readId, group] = splitSummaryKey(k);
|
|
2483
|
+
merged.push({ readId, group, delta: v });
|
|
2484
|
+
}
|
|
2485
|
+
if (merged.length) events.push({ oid: c.oid, intentId, deltas: merged });
|
|
2486
|
+
}
|
|
2487
|
+
if (!inSuffix && c.oid === fromOid) {
|
|
2488
|
+
inSuffix = true;
|
|
2489
|
+
sawFrom = true;
|
|
2490
|
+
prevs = new Map(running);
|
|
2491
|
+
extPrevs = new Map(extRunning);
|
|
2492
|
+
}
|
|
2493
|
+
}
|
|
2494
|
+
if (!sawFrom) return { ok: false, error: `fromOid ${String(fromOid).slice(0, 12)} is not on this shard's main \u2014 cannot derive the suffix` };
|
|
2495
|
+
if (!prevs) prevs = /* @__PURE__ */ new Map();
|
|
2496
|
+
const touched = /* @__PURE__ */ new Set();
|
|
2497
|
+
for (const ev of events) for (const d of ev.deltas) touched.add(summaryKey(d.readId, d.group));
|
|
2498
|
+
const subtotals = [];
|
|
2499
|
+
const keys = /* @__PURE__ */ new Set([...running.keys(), ...prevs.keys()]);
|
|
2500
|
+
for (const key of keys) {
|
|
2501
|
+
const value = running.get(key) ?? 0;
|
|
2502
|
+
const prev = prevs.get(key) ?? 0;
|
|
2503
|
+
if (value === prev && !touched.has(key)) continue;
|
|
2504
|
+
const [readId, group] = splitSummaryKey(key);
|
|
2505
|
+
const read = reads.find((r) => r.id === readId);
|
|
2506
|
+
subtotals.push({ readId, group, kind: readId === NOMOS_SHARD_ROWS_READ ? "rows" : read ? read.kind : "count", prev, value });
|
|
2507
|
+
}
|
|
2508
|
+
const extKeys = /* @__PURE__ */ new Set([...extRunning.keys(), ...extPrevs ? extPrevs.keys() : []]);
|
|
2509
|
+
for (const key of extKeys) {
|
|
2510
|
+
const value = extRunning.has(key) ? extRunning.get(key) : null;
|
|
2511
|
+
const prev = extPrevs && extPrevs.has(key) ? extPrevs.get(key) : null;
|
|
2512
|
+
if (value === prev && !touched.has(key)) continue;
|
|
2513
|
+
const [readId, group] = splitSummaryKey(key);
|
|
2514
|
+
const read = reads.find((r) => r.id === readId);
|
|
2515
|
+
if (!read) continue;
|
|
2516
|
+
subtotals.push({ readId, group, kind: read.kind, prev, value, ...extRederived.has(key) ? { rederived: true } : {} });
|
|
2517
|
+
}
|
|
2518
|
+
if (subtotals.length === 0) return { ok: true, upToDate: true, head };
|
|
2519
|
+
return {
|
|
2520
|
+
ok: true,
|
|
2521
|
+
head,
|
|
2522
|
+
payload: {
|
|
2523
|
+
fromOid: fromOid || "",
|
|
2524
|
+
toOid: head,
|
|
2525
|
+
subtotals: subtotals.sort((a, b) => summaryKey(a.readId, a.group) < summaryKey(b.readId, b.group) ? -1 : 1),
|
|
2526
|
+
events
|
|
2527
|
+
}
|
|
2528
|
+
};
|
|
2529
|
+
}
|
|
2530
|
+
function contribution(read, cur) {
|
|
2531
|
+
if (!cur || !cur.exists || cur.type !== read.of) return null;
|
|
2532
|
+
if (read.where && !evalCanonicalPred(read.where, (f) => cur.fields.get(f))) return null;
|
|
2533
|
+
const group = read.by === null ? "" : cur.fields.has(read.by) ? String(cur.fields.get(read.by)) : void 0;
|
|
2534
|
+
if (group === void 0) return null;
|
|
2535
|
+
if (read.kind === "count") return { group, value: 1 };
|
|
2536
|
+
const raw = cur.fields.get(read.field);
|
|
2537
|
+
if (isExtremumRead(read) && typeof raw !== "number") return null;
|
|
2538
|
+
return { group, value: typeof raw === "number" ? raw : 0 };
|
|
2539
|
+
}
|
|
2540
|
+
function contributionDiff(read, before, after) {
|
|
2541
|
+
const out = [];
|
|
2542
|
+
if (before && after && before.group === after.group) {
|
|
2543
|
+
const d = after.value - before.value;
|
|
2544
|
+
if (d !== 0) out.push({ readId: read.id, group: after.group, delta: d });
|
|
2545
|
+
return out;
|
|
2546
|
+
}
|
|
2547
|
+
if (before) out.push({ readId: read.id, group: before.group, delta: -before.value });
|
|
2548
|
+
if (after) out.push({ readId: read.id, group: after.group, delta: after.value });
|
|
2549
|
+
return out.filter((d) => d.delta !== 0);
|
|
2550
|
+
}
|
|
2551
|
+
function countTenantRows(eng2, ws) {
|
|
2552
|
+
let total = 0;
|
|
2553
|
+
for (const type of tenantTypes(eng2)) {
|
|
2554
|
+
total += query(eng2, ws, "aggregatesByType", JSON.stringify({ __type: type })).length;
|
|
2555
|
+
}
|
|
2556
|
+
return total;
|
|
2557
|
+
}
|
|
2558
|
+
async function countTenantRowsByHome(eng2, ws, homeKeys) {
|
|
2559
|
+
const keySet = new Set(homeKeys);
|
|
2560
|
+
const tagOf = /* @__PURE__ */ new Map();
|
|
2561
|
+
for (const hk of homeKeys) tagOf.set(await routeTagHexOfHomeKey(hk), hk);
|
|
2562
|
+
const out = /* @__PURE__ */ new Map();
|
|
2563
|
+
for (const hk of homeKeys) out.set(hk, 0);
|
|
2564
|
+
for (const type of tenantTypes(eng2)) {
|
|
2565
|
+
for (const r of query(eng2, ws, "aggregatesByType", JSON.stringify({ __type: type }))) {
|
|
2566
|
+
const id = String(r.id || "");
|
|
2567
|
+
const hk = keySet.has(id) ? id : tagOf.get(routeTagHexOfMintedId(id) ?? "");
|
|
2568
|
+
if (hk !== void 0) out.set(hk, (out.get(hk) ?? 0) + 1);
|
|
2569
|
+
}
|
|
2570
|
+
}
|
|
2571
|
+
return out;
|
|
2572
|
+
}
|
|
2573
|
+
async function collectHomedIntents(eng2, ws, homeKeys) {
|
|
2574
|
+
const routes = directiveRoutes(eng2);
|
|
2575
|
+
const tagOf = /* @__PURE__ */ new Map();
|
|
2576
|
+
for (const hk of homeKeys) tagOf.set(await routeTagHexOfHomeKey(hk), hk);
|
|
2577
|
+
const keySet = new Set(homeKeys);
|
|
2578
|
+
const gitdir = gitdirOf(ws);
|
|
2579
|
+
const head = kgit.resolveRef(eng2, gitdir, BRANCH);
|
|
2580
|
+
const commits = head ? kgit.intentsAbove(eng2, gitdir, head) : [];
|
|
2581
|
+
const docs = [];
|
|
2582
|
+
const strikeParity = /* @__PURE__ */ new Map();
|
|
2583
|
+
for (const c of commits) {
|
|
2584
|
+
let doc = null;
|
|
2585
|
+
try {
|
|
2586
|
+
const blob = bytesFromB64(c.intentB64);
|
|
2587
|
+
doc = JSON.parse(dec3.decode(blob));
|
|
2588
|
+
docs.push({ oid: c.oid, blob, doc });
|
|
2589
|
+
} catch {
|
|
2590
|
+
continue;
|
|
2591
|
+
}
|
|
2592
|
+
for (const target of doc && Array.isArray(doc.strikes) ? doc.strikes : []) {
|
|
2593
|
+
strikeParity.set(target, (strikeParity.get(target) ?? 0) + 1);
|
|
2594
|
+
}
|
|
2595
|
+
}
|
|
2596
|
+
const out = [];
|
|
2597
|
+
for (const { oid, blob, doc } of docs) {
|
|
2598
|
+
const id = doc && typeof doc.id === "string" ? doc.id : null;
|
|
2599
|
+
if (!id || (strikeParity.get(id) ?? 0) % 2 === 1) continue;
|
|
2600
|
+
const dom = doc.payload && typeof doc.payload.domain === "string" ? doc.payload.domain : null;
|
|
2601
|
+
const dirId = doc.payload && typeof doc.payload.directiveId === "string" ? doc.payload.directiveId : null;
|
|
2602
|
+
const route = dom && dirId ? routes.get(`${dom} ${dirId}`) : null;
|
|
2603
|
+
if (!route) continue;
|
|
2604
|
+
const value = doc.payload.payload ? doc.payload.payload[route.key] : void 0;
|
|
2605
|
+
if (typeof value !== "string" || !value) continue;
|
|
2606
|
+
let homeKey = null;
|
|
2607
|
+
if (route.via === "axis") homeKey = keySet.has(value) ? value : null;
|
|
2608
|
+
else {
|
|
2609
|
+
const tag = routeTagHexOfMintedId(value);
|
|
2610
|
+
homeKey = tag !== void 0 ? tagOf.get(tag) ?? null : null;
|
|
2611
|
+
}
|
|
2612
|
+
if (homeKey !== null) out.push({ oid, id, homeKey, blob });
|
|
2613
|
+
}
|
|
2614
|
+
return out;
|
|
2615
|
+
}
|
|
2616
|
+
async function migrateHomes(eng2, ws, ledger, sourceWs, homeKeys, { onlyIds = null } = {}) {
|
|
2617
|
+
const wanted = onlyIds ? new Set(onlyIds) : null;
|
|
2618
|
+
const homed = await collectHomedIntents(eng2, sourceWs, homeKeys);
|
|
2619
|
+
const main = await mainIntentIds(eng2, ws);
|
|
2620
|
+
const migrated = [], skipped = [], refused = [];
|
|
2621
|
+
for (const e of homed) {
|
|
2622
|
+
if (wanted && !wanted.has(e.id)) continue;
|
|
2623
|
+
if (main.ids.has(e.id)) {
|
|
2624
|
+
skipped.push(e.id);
|
|
2625
|
+
continue;
|
|
2626
|
+
}
|
|
2627
|
+
const res = offerAdmit(eng2, ws, e.blob);
|
|
2628
|
+
if (res.ok) {
|
|
2629
|
+
main.ids.add(e.id);
|
|
2630
|
+
main.oids.add(res.head);
|
|
2631
|
+
main.head = res.head;
|
|
2632
|
+
migrated.push(e.id);
|
|
2633
|
+
} else {
|
|
2634
|
+
refused.push({ intentId: e.id, oid: e.oid.slice(0, 10), error: String(res.error || "").slice(0, 300) });
|
|
2635
|
+
}
|
|
2636
|
+
}
|
|
2637
|
+
if (migrated.length) await commitAndPush(eng2, ws, ledger);
|
|
2638
|
+
log("migrate-homes", { ws, from: sourceWs, migrated: migrated.length, skipped: skipped.length, refused: refused.length });
|
|
2639
|
+
return { migrated, skipped, refused };
|
|
2640
|
+
}
|
|
2641
|
+
async function sealHomes(eng2, ws, ledger, { domain, lawHash, homeKeys, target, migratedIds, sealedAt }) {
|
|
2642
|
+
const have = new Set(migratedIds);
|
|
2643
|
+
const homed = await collectHomedIntents(eng2, ws, homeKeys);
|
|
2644
|
+
const missing = homed.filter((e) => !have.has(e.id)).map((e) => e.id);
|
|
2645
|
+
if (missing.length) return { ok: false, missing };
|
|
2646
|
+
const ids = homed.map((e) => e.id);
|
|
2647
|
+
const CHUNK = 1024;
|
|
2648
|
+
let head = null, struck = 0;
|
|
2649
|
+
for (let i = 0; i < Math.max(1, Math.ceil(ids.length / CHUNK)); i++) {
|
|
2650
|
+
const intentIds = ids.slice(i * CHUNK, (i + 1) * CHUNK);
|
|
2651
|
+
const res = author(eng2, ws, domain, "nomosSealHome", { homeKeys: [...homeKeys], target, intentIds, sealedAt }, lawHash, { rngDraws: 8 });
|
|
2652
|
+
if (res && res.ok === false) return { ok: false, error: String(res.error || "the law refused the seal").slice(0, 400) };
|
|
2653
|
+
head = res.head;
|
|
2654
|
+
struck += intentIds.length;
|
|
2655
|
+
}
|
|
2656
|
+
await commitAndPush(eng2, ws, ledger);
|
|
2657
|
+
log("seal-homes", { ws, target, homes: homeKeys.length, struck, head });
|
|
2658
|
+
return { ok: true, head, struck };
|
|
2659
|
+
}
|
|
2660
|
+
async function summaryCloseAgainstHeld(eng2, ws, heldRows, heldFrontier) {
|
|
2661
|
+
const head = kgit.resolveRef(eng2, gitdirOf(ws), BRANCH);
|
|
2662
|
+
if (!head) return { ok: false, error: "no local main to close against" };
|
|
2663
|
+
if (head === heldFrontier) return { ok: true, upToDate: true, head };
|
|
2664
|
+
const subtotals = [];
|
|
2665
|
+
const deltas = [];
|
|
2666
|
+
const heldHomes = heldRows.map((row) => row.data || row).filter((d) => d.readId === NOMOS_SHARD_ROWS_READ && typeof d.group === "string" && d.group !== "").map((d) => d.group);
|
|
2667
|
+
const byHome = heldHomes.length ? await countTenantRowsByHome(eng2, ws, heldHomes) : /* @__PURE__ */ new Map();
|
|
2668
|
+
for (const row of heldRows) {
|
|
2669
|
+
const d = row.data || row;
|
|
2670
|
+
if (typeof d.readId !== "string") continue;
|
|
2671
|
+
const group = typeof d.group === "string" ? d.group : "";
|
|
2672
|
+
if (d.kind === "min" || d.kind === "max") {
|
|
2673
|
+
const heldVal = d.empty === 1 || typeof d.value !== "number" ? null : d.value;
|
|
2674
|
+
const nowVal = extremum(eng2, ws, d.readId, d.kind, group).value;
|
|
2675
|
+
if (nowVal === heldVal) continue;
|
|
2676
|
+
subtotals.push({ readId: d.readId, group, kind: d.kind, prev: heldVal, value: nowVal, rederived: true });
|
|
2677
|
+
deltas.push({ readId: d.readId, group, delta: nowVal });
|
|
2678
|
+
continue;
|
|
2679
|
+
}
|
|
2680
|
+
const held = typeof d.value === "number" ? d.value : 0;
|
|
2681
|
+
let now;
|
|
2682
|
+
if (d.readId === NOMOS_SHARD_ROWS_READ) now = group === "" ? countTenantRows(eng2, ws) : byHome.get(group) ?? 0;
|
|
2683
|
+
else if (d.kind === "sum") now = sum(eng2, ws, d.readId, group).sum;
|
|
2684
|
+
else now = count(eng2, ws, d.readId, group).count;
|
|
2685
|
+
if (now === held) continue;
|
|
2686
|
+
subtotals.push({ readId: d.readId, group, kind: d.kind === "sum" || d.kind === "rows" ? d.kind : "count", prev: held, value: now });
|
|
2687
|
+
deltas.push({ readId: d.readId, group, delta: now - held });
|
|
2688
|
+
}
|
|
2689
|
+
if (subtotals.length === 0) return { ok: true, frontierOnly: true, head };
|
|
2690
|
+
return {
|
|
2691
|
+
ok: true,
|
|
2692
|
+
head,
|
|
2693
|
+
payload: {
|
|
2694
|
+
fromOid: heldFrontier || "",
|
|
2695
|
+
toOid: head,
|
|
2696
|
+
subtotals: subtotals.sort((a, b) => summaryKey(a.readId, a.group) < summaryKey(b.readId, b.group) ? -1 : 1),
|
|
2697
|
+
events: [{ oid: head, intentId: "", deltas }]
|
|
2698
|
+
}
|
|
2699
|
+
};
|
|
2700
|
+
}
|
|
2701
|
+
async function collectGlobalIntents(eng2, ws) {
|
|
2702
|
+
const globals = globalAggregates(eng2);
|
|
2703
|
+
if (globals.size === 0) return [];
|
|
2704
|
+
const gitdir = gitdirOf(ws);
|
|
2705
|
+
const head = kgit.resolveRef(eng2, gitdir, BRANCH);
|
|
2706
|
+
const commits = head ? kgit.intentsAbove(eng2, gitdir, head) : [];
|
|
2707
|
+
const typeOf = /* @__PURE__ */ new Map();
|
|
2708
|
+
const strikeParity = /* @__PURE__ */ new Map();
|
|
2709
|
+
const docs = [];
|
|
2710
|
+
for (const c of commits) {
|
|
2711
|
+
let blob = null, doc = null;
|
|
2712
|
+
try {
|
|
2713
|
+
blob = bytesFromB64(c.intentB64);
|
|
2714
|
+
doc = JSON.parse(dec3.decode(blob));
|
|
2715
|
+
} catch {
|
|
2716
|
+
continue;
|
|
2717
|
+
}
|
|
2718
|
+
docs.push({ oid: c.oid, blob, doc });
|
|
2719
|
+
for (const target of doc && Array.isArray(doc.strikes) ? doc.strikes : []) {
|
|
2720
|
+
strikeParity.set(target, (strikeParity.get(target) ?? 0) + 1);
|
|
2721
|
+
}
|
|
2722
|
+
}
|
|
2723
|
+
const out = [];
|
|
2724
|
+
for (const { oid, blob, doc } of docs) {
|
|
2725
|
+
const id = doc && typeof doc.id === "string" ? doc.id : null;
|
|
2726
|
+
let touches = false;
|
|
2727
|
+
for (const ev of parseIntentEvents(blob)) {
|
|
2728
|
+
const newType = ev.sets.get("__type");
|
|
2729
|
+
if (newType !== void 0 && !typeOf.has(ev.aggregate)) typeOf.set(ev.aggregate, newType);
|
|
2730
|
+
const t = newType ?? typeOf.get(ev.aggregate);
|
|
2731
|
+
if (t !== void 0 && globals.has(t)) touches = true;
|
|
2732
|
+
}
|
|
2733
|
+
if (!touches || !id || (strikeParity.get(id) ?? 0) % 2 === 1) continue;
|
|
2734
|
+
out.push({ oid, id, blob });
|
|
2735
|
+
}
|
|
2736
|
+
return out;
|
|
2737
|
+
}
|
|
2738
|
+
async function replicateIntents(eng2, ws, ledger, intents, { push = true } = {}) {
|
|
2739
|
+
const main = await mainIntentIds(eng2, ws);
|
|
2740
|
+
const replicated = [], skipped = [], refused = [];
|
|
2741
|
+
for (const e of intents) {
|
|
2742
|
+
if (e.id && main.ids.has(e.id)) {
|
|
2743
|
+
skipped.push(e.id);
|
|
2744
|
+
continue;
|
|
2745
|
+
}
|
|
2746
|
+
const res = offerAdmit(eng2, ws, e.blob);
|
|
2747
|
+
if (res.ok) {
|
|
2748
|
+
if (e.id) main.ids.add(e.id);
|
|
2749
|
+
main.oids.add(res.head);
|
|
2750
|
+
main.head = res.head;
|
|
2751
|
+
replicated.push(e.id || res.head);
|
|
2752
|
+
} else {
|
|
2753
|
+
refused.push({ intentId: e.id || "", error: String(res.error || "").slice(0, 300) });
|
|
2754
|
+
}
|
|
2755
|
+
}
|
|
2756
|
+
if (replicated.length && push) await commitAndPush(eng2, ws, ledger);
|
|
2757
|
+
log("replicate-globals", { ws, replicated: replicated.length, skipped: skipped.length, refused: refused.length });
|
|
2758
|
+
return { replicated, skipped, refused };
|
|
2759
|
+
}
|
|
2760
|
+
async function admitIntentOffers(eng2, ws, ledger, offers, { receiptFence = null, shardRouting = null, clientBase = null } = {}) {
|
|
2761
|
+
const main = await mainIntentIds(eng2, ws);
|
|
2762
|
+
const sessions = /* @__PURE__ */ new Map();
|
|
2763
|
+
const deadLetters = [];
|
|
2764
|
+
let admittedTotal = 0;
|
|
2765
|
+
const born = [];
|
|
2766
|
+
const birthOutcomes = [];
|
|
2767
|
+
const timings = { spans: [], intents: 0, sessions: 0 };
|
|
2768
|
+
const summaryCtx = shardRouting && shardRouting.selfLabel && scopedReads(eng2).length > 0 ? { reads: scopedReads(eng2), prevs: /* @__PURE__ */ new Map(), values: /* @__PURE__ */ new Map(), events: [], rowsDelta: 0, rederived: /* @__PURE__ */ new Set(), rowsByHome: /* @__PURE__ */ new Map(), homeTags: /* @__PURE__ */ new Map(), homeKeySet: /* @__PURE__ */ new Set() } : null;
|
|
2769
|
+
if (summaryCtx) {
|
|
2770
|
+
try {
|
|
2771
|
+
for (const r of query(eng2, ws, "aggregatesByType", JSON.stringify({ __type: "NomosHomeReceipt" }))) {
|
|
2772
|
+
const hk = r.data && typeof r.data.homeKey === "string" ? r.data.homeKey : null;
|
|
2773
|
+
if (!hk) continue;
|
|
2774
|
+
summaryCtx.homeKeySet.add(hk);
|
|
2775
|
+
summaryCtx.homeTags.set(await routeTagHexOfHomeKey(hk), hk);
|
|
2776
|
+
}
|
|
2777
|
+
} catch (e) {
|
|
2778
|
+
log("home-tag-table-error", { ws, error: String(e).slice(0, 200) });
|
|
2779
|
+
}
|
|
2780
|
+
}
|
|
2781
|
+
const gitdir0 = gitdirOf(ws);
|
|
2782
|
+
const headBefore = summaryCtx ? kgit.resolveRef(eng2, gitdir0, BRANCH) : null;
|
|
2783
|
+
const t0 = Date.now();
|
|
2784
|
+
let seq = 0;
|
|
2785
|
+
const span = (name, kind, depth = 1, extra = {}) => {
|
|
2786
|
+
const s0 = Date.now();
|
|
2787
|
+
return (end = {}) => timings.spans.push({ id: `intent-offer-${t0}-${seq++}`, track: "cloud", name, kind, depth, t0: s0, t1: Date.now(), ...extra, ...end });
|
|
2788
|
+
};
|
|
2789
|
+
for (const offer of Array.isArray(offers) ? offers : []) {
|
|
2790
|
+
const cid = typeof offer?.session === "string" && offer.session ? offer.session : "direct";
|
|
2791
|
+
const rec = sessions.get(cid) || { session: cid, admitted: [], rejected: [], skipped: [] };
|
|
2792
|
+
sessions.set(cid, rec);
|
|
2793
|
+
const done = span(`intent offer ${cid.slice(0, 14)}`, "cadmit", 1, { client: cid });
|
|
2794
|
+
let blob = offer?.blob instanceof Uint8Array ? offer.blob : null;
|
|
2795
|
+
try {
|
|
2796
|
+
if (!blob && typeof offer?.intentB64 === "string") blob = Uint8Array.from(atob(offer.intentB64), (c) => c.charCodeAt(0));
|
|
2797
|
+
const maxOfferBytes = offer?.staged ? 64 * 1024 * 1024 : 32 * 1024 * 1024;
|
|
2798
|
+
if (!blob || blob.byteLength === 0 || blob.byteLength > maxOfferBytes) {
|
|
2799
|
+
rec.rejected.push({ oid: String(offer?.oid || "").slice(0, 10), error: `intent offer must carry intentB64 <= ${maxOfferBytes / (1024 * 1024)} MiB` });
|
|
2800
|
+
continue;
|
|
2801
|
+
}
|
|
2802
|
+
let doc = null;
|
|
2803
|
+
try {
|
|
2804
|
+
doc = JSON.parse(dec3.decode(blob));
|
|
2805
|
+
} catch {
|
|
2806
|
+
}
|
|
2807
|
+
const intentId = typeof doc?.id === "string" ? doc.id : null;
|
|
2808
|
+
const dom = typeof doc?.payload?.domain === "string" ? doc.payload.domain : null;
|
|
2809
|
+
const dirId = typeof doc?.payload?.directiveId === "string" ? doc.payload.directiveId : null;
|
|
2810
|
+
const oid = String(offer?.oid || intentId || "").slice(0, 10);
|
|
2811
|
+
if (!dom) {
|
|
2812
|
+
rec.rejected.push({ oid, error: "non-authored intent \u2014 governance enters its own admin lane" });
|
|
2813
|
+
continue;
|
|
2814
|
+
}
|
|
2815
|
+
if (receiptFence && dirId && Array.isArray(receiptFence[dom]) && receiptFence[dom].includes(dirId)) {
|
|
2816
|
+
const error = `'${dom}/${dirId}' is a capability RECEIPT \u2014 receipts are executor/owner writes, never open-session writes. Author it via POST /v1/workspaces/${ws}/author (owner Bearer), or \u2014 if this parked receipt is sanctioned \u2014 the owner unjams it: POST /v1/workspaces/${ws}/dead-letters/retry`;
|
|
2817
|
+
rec.rejected.push({ oid, error, deadLettered: true });
|
|
2818
|
+
deadLetters.push({ intentId, oid, session: cid, error, blob, domain: dom, directiveId: dirId });
|
|
2819
|
+
continue;
|
|
2820
|
+
}
|
|
2821
|
+
if (intentId && main.ids.has(intentId)) {
|
|
2822
|
+
rec.skipped.push({ oid, intent: intentId.slice(0, 12), alreadyOnMain: true });
|
|
2823
|
+
continue;
|
|
2824
|
+
}
|
|
2825
|
+
const cap = summaryCtx ? beginSummaryCapture(eng2, ws, summaryCtx, blob) : null;
|
|
2826
|
+
const _tOffer = Date.now();
|
|
2827
|
+
const res = offerAdmit(eng2, ws, blob, { defer: false });
|
|
2828
|
+
log("offer-admit-ms", { ws, ms: Date.now() - _tOffer, deferred: false, ok: res.ok });
|
|
2829
|
+
if (res.ok) {
|
|
2830
|
+
if (intentId) main.ids.add(intentId);
|
|
2831
|
+
main.oids.add(res.head);
|
|
2832
|
+
main.head = res.head;
|
|
2833
|
+
rec.admitted.push({ oid, head: res.head });
|
|
2834
|
+
admittedTotal += 1;
|
|
2835
|
+
if (Array.isArray(res.born) && res.born.length) born.push(...res.born);
|
|
2836
|
+
if (Array.isArray(res.birthOutcomes) && res.birthOutcomes.length) birthOutcomes.push(...res.birthOutcomes);
|
|
2837
|
+
if (cap) finishSummaryCapture(eng2, ws, summaryCtx, cap, res.head ?? "", intentId);
|
|
2838
|
+
} else {
|
|
2839
|
+
const error = `${String(res.error || "").slice(0, 300)} \u2014 dead-lettered; fix the law (or the payload), then POST /v1/workspaces/${ws}/dead-letters/retry`;
|
|
2840
|
+
rec.rejected.push({ oid, error, deadLettered: true });
|
|
2841
|
+
deadLetters.push({ intentId, oid, session: cid, error, blob, domain: dom, directiveId: dirId });
|
|
2842
|
+
}
|
|
2843
|
+
} catch (e) {
|
|
2844
|
+
rec.rejected.push({ oid: String(offer?.oid || "").slice(0, 10), error: String(e).slice(0, 200) });
|
|
2845
|
+
} finally {
|
|
2846
|
+
done();
|
|
2847
|
+
}
|
|
2848
|
+
}
|
|
2849
|
+
timings.sessions = sessions.size;
|
|
2850
|
+
timings.intents = admittedTotal;
|
|
2851
|
+
if (deadLetters.length) {
|
|
2852
|
+
try {
|
|
2853
|
+
await materializeRefused(eng2, ws, ledger, deadLetters);
|
|
2854
|
+
} catch (e) {
|
|
2855
|
+
log("refused-materialize-failed", { ws, error: String(e).slice(0, 200) });
|
|
2856
|
+
}
|
|
2857
|
+
}
|
|
2858
|
+
if (admittedTotal) {
|
|
2859
|
+
const sealDone = span("seal offered intents", "cseal", 1, { admitted: admittedTotal });
|
|
2860
|
+
await commitAndPush(eng2, ws, ledger);
|
|
2861
|
+
sealDone();
|
|
2862
|
+
}
|
|
2863
|
+
const gitdir = gitdirOf(ws);
|
|
2864
|
+
const head = kgit.resolveRef(eng2, gitdir, BRANCH) ?? main.head;
|
|
2865
|
+
const summary = summaryCtx ? summaryOfCapture(summaryCtx, headBefore, head) : null;
|
|
2866
|
+
let resultPackB64 = null;
|
|
2867
|
+
if (admittedTotal && head && /^[0-9a-f]{40}$/i.test(clientBase || "") && clientBase !== head) {
|
|
2868
|
+
try {
|
|
2869
|
+
const p = servePack(eng2, ws, clientBase ?? "");
|
|
2870
|
+
if (p && p.packB64 && p.bytes && p.bytes <= RESULT_PACK_INLINE_MAX) resultPackB64 = p.packB64;
|
|
2871
|
+
} catch {
|
|
2872
|
+
}
|
|
2873
|
+
}
|
|
2874
|
+
log("admit-intent-offers", { ws, sessions: sessions.size, admitted: admittedTotal, rejected: [...sessions.values()].reduce((n, s) => n + s.rejected.length, 0), resultPack: resultPackB64 ? "inline" : "none" });
|
|
2875
|
+
const bornPacked = born.length ? packBornChildren(eng2, born) : born;
|
|
2876
|
+
return { sessions: [...sessions.values()].map((s) => ({ ...s, ...s.skipped.length ? {} : { skipped: void 0 } })), main: head, deadLetters, timings, summary: summary ?? null, born: bornPacked, birthOutcomes, ...resultPackB64 ? { resultPackB64, resultBase: clientBase } : {} };
|
|
2877
|
+
}
|
|
2878
|
+
async function checkpointStateHash(eng2, ws) {
|
|
2879
|
+
const rows = query(eng2, ws, "aggregatesByType", JSON.stringify({ __type: "NomosSummarySubtotal" }));
|
|
2880
|
+
const canon = rows.map((r) => {
|
|
2881
|
+
const d = r.data || {};
|
|
2882
|
+
return { id: r.id, readId: d.readId, group: d.group, shard: d.shard, kind: d.kind, value: d.value, frontier: d.frontier };
|
|
2883
|
+
}).sort((a, b) => a.id < b.id ? -1 : a.id > b.id ? 1 : 0);
|
|
2884
|
+
return sha256hex(JSON.stringify(canon));
|
|
2885
|
+
}
|
|
2886
|
+
async function authorOn(eng2, ws, ledger, domain, directiveId, payload, lawHash, opts = {}) {
|
|
2887
|
+
await mountWorkspace(eng2, ws, ledger);
|
|
2888
|
+
let res;
|
|
2889
|
+
try {
|
|
2890
|
+
res = author(eng2, ws, domain, directiveId, payload, lawHash, opts);
|
|
2891
|
+
} catch (e) {
|
|
2892
|
+
return { ok: false, error: String(e?.message || e).slice(0, 500) };
|
|
2893
|
+
}
|
|
2894
|
+
if (res && res.ok === false) return { ok: false, error: String(res.error || "the law refused this intent").slice(0, 500) };
|
|
2895
|
+
await commitAndPush(eng2, ws, ledger);
|
|
2896
|
+
const bornPacked = Array.isArray(res.born) && res.born.length ? packBornChildren(eng2, res.born) : res.born;
|
|
2897
|
+
return { ok: true, head: res.head, born: bornPacked ?? [], birthOutcomes: res.birthOutcomes ?? [] };
|
|
2898
|
+
}
|
|
2899
|
+
async function deepVerifyShard(eng2, shardWs, shardLedger, coordinatorRows) {
|
|
2900
|
+
const replay = await verifyChain(eng2, shardWs, shardLedger);
|
|
2901
|
+
if (!replay.valid) {
|
|
2902
|
+
return { verdict: `invalid:${replay.check || "replay"}`, replay, checks: [] };
|
|
2903
|
+
}
|
|
2904
|
+
await mountWorkspace(eng2, shardWs, shardLedger);
|
|
2905
|
+
const checks = [];
|
|
2906
|
+
let allMatch = true;
|
|
2907
|
+
const heldHomes = coordinatorRows.map((row) => row.data || row).filter((d) => d.readId === NOMOS_SHARD_ROWS_READ && typeof d.group === "string" && d.group !== "").map((d) => d.group);
|
|
2908
|
+
const byHome = heldHomes.length ? await countTenantRowsByHome(eng2, shardWs, heldHomes) : /* @__PURE__ */ new Map();
|
|
2909
|
+
for (const row of coordinatorRows) {
|
|
2910
|
+
const d = row.data || row;
|
|
2911
|
+
if (typeof d.readId !== "string") continue;
|
|
2912
|
+
const group = d.group ?? "";
|
|
2913
|
+
if (d.kind === "min" || d.kind === "max") {
|
|
2914
|
+
const heldVal = d.empty === 1 || typeof d.value !== "number" ? null : d.value;
|
|
2915
|
+
const recomputed2 = extremum(eng2, shardWs, d.readId, d.kind, group).value;
|
|
2916
|
+
const match2 = recomputed2 === heldVal;
|
|
2917
|
+
if (!match2) allMatch = false;
|
|
2918
|
+
checks.push({ readId: d.readId, group, kind: d.kind, coordinator: heldVal, shard: recomputed2, match: match2 });
|
|
2919
|
+
continue;
|
|
2920
|
+
}
|
|
2921
|
+
let recomputed;
|
|
2922
|
+
if (d.readId === NOMOS_SHARD_ROWS_READ) {
|
|
2923
|
+
recomputed = group === "" ? countTenantRows(eng2, shardWs) : byHome.get(group) ?? 0;
|
|
2924
|
+
} else if (d.kind === "sum") {
|
|
2925
|
+
recomputed = sum(eng2, shardWs, d.readId, group).sum;
|
|
2926
|
+
} else {
|
|
2927
|
+
recomputed = count(eng2, shardWs, d.readId, group).count;
|
|
2928
|
+
}
|
|
2929
|
+
const match = recomputed === d.value;
|
|
2930
|
+
if (!match) allMatch = false;
|
|
2931
|
+
checks.push({ readId: d.readId, group, coordinator: d.value, shard: recomputed, match });
|
|
2932
|
+
}
|
|
2933
|
+
const head = kgit.resolveRef(eng2, gitdirOf(shardWs), BRANCH);
|
|
2934
|
+
return {
|
|
2935
|
+
verdict: allMatch ? "verified" : "mismatch",
|
|
2936
|
+
head,
|
|
2937
|
+
intents: replay.intents,
|
|
2938
|
+
plansRerun: replay.plansRerun,
|
|
2939
|
+
checks
|
|
2940
|
+
};
|
|
2941
|
+
}
|
|
2942
|
+
function stageWorkspaceDir(eng2, ws) {
|
|
2943
|
+
const wsContents = /* @__PURE__ */ new Map();
|
|
2944
|
+
dirOf(eng2.preopen.dir.contents.get("ws")).contents.set(ws, new Directory(wsContents));
|
|
2945
|
+
}
|
|
2946
|
+
async function mountWorkspace(eng2, ws, ledger) {
|
|
2947
|
+
if (eng2.mounted.has(ws)) return eng2.mounted.get(ws);
|
|
2948
|
+
stageWorkspaceDir(eng2, ws);
|
|
2949
|
+
const gitdir = gitdirOf(ws);
|
|
2950
|
+
let restored = false, remoteMain = null, restoreError = null;
|
|
2951
|
+
try {
|
|
2952
|
+
await custodySkeleton(eng2, gitdir);
|
|
2953
|
+
remoteMain = await custodyFetch(eng2, ws, ledger, { remoteRef: "refs/heads/main" });
|
|
2954
|
+
if (remoteMain) {
|
|
2955
|
+
kgit.writeRef(eng2, gitdir, "HEAD", "ref: refs/heads/main");
|
|
2956
|
+
restored = true;
|
|
2957
|
+
}
|
|
2958
|
+
} catch (e) {
|
|
2959
|
+
restoreError = String(e?.stack || e).split("\n").slice(0, 5).join(" | ");
|
|
2960
|
+
}
|
|
2961
|
+
const m = { restored, remoteMain, restoreError };
|
|
2962
|
+
eng2.mounted.set(ws, m);
|
|
2963
|
+
return m;
|
|
2964
|
+
}
|
|
2965
|
+
function assertAuthored(res, what) {
|
|
2966
|
+
if (!res || res.ok === false) throw new Error(`${what}: ${String(res && res.error || "law refused the intent").slice(0, 500)}`);
|
|
2967
|
+
return res;
|
|
2968
|
+
}
|
|
2969
|
+
async function mountEphemeralWorkspace(eng2, ws, { domainHash = "", packageUsda = "" } = {}) {
|
|
2970
|
+
let m = eng2.mounted.get(ws);
|
|
2971
|
+
if (!m) {
|
|
2972
|
+
stageWorkspaceDir(eng2, ws);
|
|
2973
|
+
m = { restored: false, remoteMain: null, restoreError: null, ephemeral: true };
|
|
2974
|
+
eng2.mounted.set(ws, m);
|
|
2975
|
+
}
|
|
2976
|
+
if (!nomosActive(eng2, ws)) {
|
|
2977
|
+
assertAuthored(
|
|
2978
|
+
author(eng2, ws, "bootstrap", "installDomain", installPayload(eng2.hashes.nomos, eng2.nomosPkg, eng2.installedBy), "", { deferProjection: false, rngDraws: 8 }),
|
|
2979
|
+
`ephemeral workspace '${ws}' bootstrap`
|
|
2980
|
+
);
|
|
2981
|
+
}
|
|
2982
|
+
if (domainHash && packageUsda && qById(eng2, ws, `domain-installation:${domainHash}`).length === 0) {
|
|
2983
|
+
assertAuthored(
|
|
2984
|
+
author(eng2, ws, "nomos", "installDomain", installPayload(domainHash, packageUsda, eng2.installedBy), eng2.hashes.nomos, { deferProjection: false, rngDraws: 8 }),
|
|
2985
|
+
`ephemeral workspace '${ws}' installDomain(${domainHash.slice(0, 12)})`
|
|
2986
|
+
);
|
|
2987
|
+
}
|
|
2988
|
+
return m;
|
|
2989
|
+
}
|
|
2990
|
+
async function authorEphemeral(eng2, ws, mountOpts, domain, directiveId, payload, lawHash, opts = {}) {
|
|
2991
|
+
await mountEphemeralWorkspace(eng2, ws, mountOpts);
|
|
2992
|
+
return author(eng2, ws, domain, directiveId, payload, lawHash, { deferProjection: false, ...opts });
|
|
2993
|
+
}
|
|
2994
|
+
async function queryEphemeral(eng2, ws, mountOpts, queryId, paramsJson, principal = "", keys = []) {
|
|
2995
|
+
await mountEphemeralWorkspace(eng2, ws, mountOpts);
|
|
2996
|
+
return query(eng2, ws, queryId, paramsJson, principal, keys);
|
|
2997
|
+
}
|
|
2998
|
+
function unmount(eng2, ws) {
|
|
2999
|
+
eng2.mounted.delete(ws);
|
|
3000
|
+
eng2.mainIntents.delete(ws);
|
|
3001
|
+
const wsRoot = dirOf(eng2.preopen.dir.contents.get("ws"));
|
|
3002
|
+
if (wsRoot) wsRoot.contents.delete(ws);
|
|
3003
|
+
}
|
|
3004
|
+
function writeWork(eng2, name, bytes) {
|
|
3005
|
+
eng2.preopen.dir.contents.set(name, new File(bytes));
|
|
3006
|
+
}
|
|
3007
|
+
function currentLaw(eng2, ws) {
|
|
3008
|
+
const out = JSON.parse(call(eng2.ex, "query", { repoArg: repoArgOf(ws), workspace: ws, queryBytes: b64Json({ op: "currentLaw" }), branch: BRANCH }, eng2.STDERR));
|
|
3009
|
+
return out.keyMap || {};
|
|
3010
|
+
}
|
|
3011
|
+
function installedLaw(eng2, ws) {
|
|
3012
|
+
return JSON.parse(call(eng2.ex, "query", { repoArg: repoArgOf(ws), workspace: ws, queryBytes: b64Json({ op: "installedLaw" }), branch: BRANCH }, eng2.STDERR));
|
|
3013
|
+
}
|
|
3014
|
+
function kernelStamp(eng2, ws) {
|
|
3015
|
+
return JSON.parse(call(eng2.ex, "query", {
|
|
3016
|
+
repoArg: repoArgOf(ws),
|
|
3017
|
+
workspace: ws,
|
|
3018
|
+
queryBytes: b64Json({ op: "kernelStamp" }),
|
|
3019
|
+
branch: BRANCH
|
|
3020
|
+
}, eng2.STDERR));
|
|
3021
|
+
}
|
|
3022
|
+
function domainPackage(eng2, ws, hash) {
|
|
3023
|
+
return JSON.parse(call(eng2.ex, "query", { repoArg: repoArgOf(ws), workspace: ws, queryBytes: b64Json({ op: "domainPackage", hash }), branch: BRANCH }, eng2.STDERR));
|
|
3024
|
+
}
|
|
3025
|
+
function declaredCapabilities(eng2, ws) {
|
|
3026
|
+
const out = JSON.parse(call(eng2.ex, "query", { repoArg: repoArgOf(ws), workspace: ws, queryBytes: b64Json({ op: "declaredCapabilities" }), branch: BRANCH }, eng2.STDERR));
|
|
3027
|
+
return out.capabilities || [];
|
|
3028
|
+
}
|
|
3029
|
+
function reactorScan(eng2, ws) {
|
|
3030
|
+
return JSON.parse(call(eng2.ex, "outbox", { repoArg: repoArgOf(ws), branch: BRANCH }, eng2.STDERR));
|
|
3031
|
+
}
|
|
3032
|
+
function evolveDryRun(eng2, ws, usda, domainHash, dispositions = null) {
|
|
3033
|
+
const r = author(eng2, ws, "nomos", "installDomain", installPayload(domainHash, usda, eng2.installedBy, dispositions), eng2.hashes.nomos, { dryRun: true, deferProjection: false });
|
|
3034
|
+
return r.ok ? { admits: true } : { admits: false, detail: r.error };
|
|
3035
|
+
}
|
|
3036
|
+
function mintId(eng2, typeTag) {
|
|
3037
|
+
return JSON.parse(call(eng2.ex, "query", { queryBytes: b64Json({ op: "mint", typeTag, nowMillis: Date.now() }) }, eng2.STDERR)).id;
|
|
3038
|
+
}
|
|
3039
|
+
function author(eng2, ws, domain, directiveId, payload, controllerHash, opts = {}) {
|
|
3040
|
+
const seq = eng2.seq++;
|
|
3041
|
+
const capPorts = { clock: { physical: Date.now(), logical: 0, replica: eng2.replica }, rng: osEntropyBuffer(opts.rngDraws ?? 64) };
|
|
3042
|
+
if (opts.__nomosConflictSnapshots !== void 0 && opts.__nomosConflictSnapshots.length > 0) {
|
|
3043
|
+
capPorts.conflict_snapshots = opts.__nomosConflictSnapshots;
|
|
3044
|
+
}
|
|
3045
|
+
if (opts.validFromEpoch !== void 0) capPorts.valid_from_epoch = opts.validFromEpoch;
|
|
3046
|
+
if (opts.principalVerified !== void 0) {
|
|
3047
|
+
capPorts.principal = opts.actor ?? "";
|
|
3048
|
+
capPorts.principal_verified = !!opts.principalVerified;
|
|
3049
|
+
}
|
|
3050
|
+
const envelope = { payload: { domain, directiveId, payload }, captured_ports: capPorts, policy_version: 1, policy_domain: "Nomos", policy_gas: 0, policy_memory: 0 };
|
|
3051
|
+
if (opts.attestations !== void 0) envelope.attestations = opts.attestations;
|
|
3052
|
+
writeWork(eng2, `payload-${seq}.json`, enc3.encode(JSON.stringify(payload)));
|
|
3053
|
+
writeWork(eng2, `envelope-${seq}.json`, enc3.encode(stringifyBig(envelope)));
|
|
3054
|
+
const genesis = !controllerHash;
|
|
3055
|
+
const attestorSecretFor = (from) => {
|
|
3056
|
+
const s = opts.attestorSecrets;
|
|
3057
|
+
if (!s) return null;
|
|
3058
|
+
if (typeof s === "string") return s;
|
|
3059
|
+
return typeof s[from] === "string" ? s[from] : null;
|
|
3060
|
+
};
|
|
3061
|
+
const defer = opts.deferProjection !== false;
|
|
3062
|
+
const offerOnce = () => JSON.parse(call(eng2.ex, "offer", { repoArg: repoArgOf(ws), workspace: ws, domain, directiveId, payloadFile: `/work/payload-${seq}.json`, envelopeFile: `/work/envelope-${seq}.json`, seq, actor: opts.actor ?? "", ...opts.authToken ? { authToken: opts.authToken } : {}, domainFile: genesis ? "/work/domain.package.usda" : "", domainHash: genesis ? "" : controllerHash, branch: BRANCH, ...opts.authorSecret ? { authorSecret: opts.authorSecret } : {}, ...opts.spans ? { spans: true } : {}, ...opts.dryRun ? { dryRun: true } : defer ? { deferProjection: true } : {} }, eng2.STDERR));
|
|
3063
|
+
let v = offerOnce();
|
|
3064
|
+
for (let pass = 0; pass < 8 && v.outcome === "refused"; pass++) {
|
|
3065
|
+
const need = parseAttestedReadNeed(v.verdict?.reason ?? v.error);
|
|
3066
|
+
if (!need || !eng2.mounted.has(need.from)) break;
|
|
3067
|
+
const secret = attestorSecretFor(need.from);
|
|
3068
|
+
if (!secret) break;
|
|
3069
|
+
const served = attestedRead(eng2, need.from, {
|
|
3070
|
+
queryId: need.queryId,
|
|
3071
|
+
paramsJson: JSON.stringify(need.args),
|
|
3072
|
+
attestorSecret: secret,
|
|
3073
|
+
nowMs: opts.attestNowMs ?? Date.now(),
|
|
3074
|
+
// K3 (§1.5.3): colocated custody is in-memory — always ask for the carried enrollment
|
|
3075
|
+
// proof; the kernel produces-or-omits under its own bounds (the adapter decides nothing).
|
|
3076
|
+
includeRosterProof: true
|
|
3077
|
+
});
|
|
3078
|
+
if (!served || served.ok !== true) break;
|
|
3079
|
+
envelope.attestations = [...envelope.attestations ?? [], { queryId: need.queryId, result: served.rows, attestation: served.attestation }];
|
|
3080
|
+
writeWork(eng2, `envelope-${seq}.json`, enc3.encode(stringifyBig(envelope)));
|
|
3081
|
+
v = offerOnce();
|
|
3082
|
+
}
|
|
3083
|
+
const res = v.outcome === "admitted" ? { ok: true, head: v.head, intentOut: v.intentOut, born: v.born ?? [], birthOutcomes: v.birthOutcomes ?? [], ...v.kernelSpans ? { kernelSpans: v.kernelSpans } : {} } : v.outcome === "refused" ? { ok: false, error: v.verdict?.reason ?? v.error } : v;
|
|
3084
|
+
if (defer && res.ok) (eng2.pendingProjection ??= /* @__PURE__ */ new Set()).add(ws);
|
|
3085
|
+
return res;
|
|
3086
|
+
}
|
|
3087
|
+
function materializeGenesis(eng2, ws, genesisChain, opts = {}) {
|
|
3088
|
+
return JSON.parse(call(eng2.ex, "materialize_genesis", {
|
|
3089
|
+
repoArg: repoArgOf(ws),
|
|
3090
|
+
workspace: ws,
|
|
3091
|
+
genesisChain,
|
|
3092
|
+
clockMs: opts.clockMs ?? 17e11,
|
|
3093
|
+
replica: eng2.replica,
|
|
3094
|
+
rngSeed: opts.rngSeed ?? 1,
|
|
3095
|
+
branch: BRANCH
|
|
3096
|
+
}, eng2.STDERR));
|
|
3097
|
+
}
|
|
3098
|
+
function setTestKRoot(eng2, kRootPublicB64) {
|
|
3099
|
+
return JSON.parse(call(eng2.ex, "set_test_k_root", { kRootPublicB64 }, eng2.STDERR));
|
|
3100
|
+
}
|
|
3101
|
+
function refreshProjection(eng2, ws) {
|
|
3102
|
+
return JSON.parse(call(eng2.ex, "query", { repoArg: repoArgOf(ws), workspace: ws, queryBytes: b64Json({ op: "refresh" }), branch: BRANCH }, eng2.STDERR));
|
|
3103
|
+
}
|
|
3104
|
+
function flushDeferredProjections(eng2) {
|
|
3105
|
+
for (const ws of eng2.pendingProjection ?? []) {
|
|
3106
|
+
const _t = Date.now();
|
|
3107
|
+
try {
|
|
3108
|
+
refreshProjection(eng2, ws);
|
|
3109
|
+
} catch {
|
|
3110
|
+
}
|
|
3111
|
+
log("flush-projection-ms", { ws, ms: Date.now() - _t });
|
|
3112
|
+
}
|
|
3113
|
+
if (eng2.pendingProjection) eng2.pendingProjection.clear();
|
|
3114
|
+
}
|
|
3115
|
+
function exportCheckpoint(eng2, ws) {
|
|
3116
|
+
return JSON.parse(call(eng2.ex, "checkpoint_export", { repoArg: repoArgOf(ws), workspace: ws, branch: BRANCH }, eng2.STDERR));
|
|
3117
|
+
}
|
|
3118
|
+
function importCheckpoint(eng2, ws, blobB64, frontierB64, stateRoot) {
|
|
3119
|
+
return JSON.parse(call(eng2.ex, "checkpoint_import", { repoArg: repoArgOf(ws), workspace: ws, branch: BRANCH, blob: blobB64, ...frontierB64 ? { frontier: frontierB64 } : {}, ...stateRoot ? { stateRoot } : {} }, eng2.STDERR));
|
|
3120
|
+
}
|
|
3121
|
+
var CKPT_REF = (lawHash) => `refs/meta/checkpoint/${lawHash}`;
|
|
3122
|
+
async function streamAll(stream) {
|
|
3123
|
+
const chunks = [];
|
|
3124
|
+
const reader = stream.getReader();
|
|
3125
|
+
for (; ; ) {
|
|
3126
|
+
const { done, value } = await reader.read();
|
|
3127
|
+
if (done) break;
|
|
3128
|
+
chunks.push(value);
|
|
3129
|
+
}
|
|
3130
|
+
let len = 0;
|
|
3131
|
+
for (const c of chunks) len += c.length;
|
|
3132
|
+
const out = new Uint8Array(len);
|
|
3133
|
+
let at = 0;
|
|
3134
|
+
for (const c of chunks) {
|
|
3135
|
+
out.set(c, at);
|
|
3136
|
+
at += c.length;
|
|
3137
|
+
}
|
|
3138
|
+
return out;
|
|
3139
|
+
}
|
|
3140
|
+
async function gzip(bytes) {
|
|
3141
|
+
const cs = new CompressionStream("gzip");
|
|
3142
|
+
return streamAll(new Response(bytes).body.pipeThrough(cs));
|
|
3143
|
+
}
|
|
3144
|
+
async function gunzip(bytes) {
|
|
3145
|
+
const ds = new DecompressionStream("gzip");
|
|
3146
|
+
return streamAll(new Response(bytes).body.pipeThrough(ds));
|
|
3147
|
+
}
|
|
3148
|
+
async function writeCheckpointObjects(eng2, ws, ex, lawHash) {
|
|
3149
|
+
const gitdir = gitdirOf(ws);
|
|
3150
|
+
const files = [];
|
|
3151
|
+
let rawLen = 0, gzLen = 0;
|
|
3152
|
+
if (ex.blob) {
|
|
3153
|
+
const gz = await gzip(enc3.encode(ex.blob));
|
|
3154
|
+
rawLen = ex.blob.length;
|
|
3155
|
+
gzLen = gz.length;
|
|
3156
|
+
files.push({ path: "checkpoint", bytes: gz });
|
|
3157
|
+
}
|
|
3158
|
+
let fgzLen = 0;
|
|
3159
|
+
if (ex.frontier) {
|
|
3160
|
+
const fgz = await gzip(enc3.encode(ex.frontier));
|
|
3161
|
+
fgzLen = fgz.length;
|
|
3162
|
+
files.push({ path: "frontier", bytes: fgz });
|
|
3163
|
+
}
|
|
3164
|
+
files.push({ path: "meta", bytes: enc3.encode(JSON.stringify({ cursor: ex.cursor, bytes: ex.bytes ?? 0, lawHash, codec: "gzip", raw: rawLen, stored: gzLen, frontierStored: fgzLen, ...ex.stateRoot ? { stateRoot: ex.stateRoot } : {} })) });
|
|
3165
|
+
const ref = CKPT_REF(lawHash);
|
|
3166
|
+
const commitOid = kgit.writeMetaCommit(eng2, gitdir, { parents: [], files, message: `checkpoint ${ws} @${String(ex.cursor).slice(0, 16)}`, timeSecs: 0, ref });
|
|
3167
|
+
return { ref, commitOid };
|
|
3168
|
+
}
|
|
3169
|
+
async function readCheckpointBlob(eng2, ws, commitOid) {
|
|
3170
|
+
const gitdir = gitdirOf(ws);
|
|
3171
|
+
let codec = "gzip";
|
|
3172
|
+
let stateRoot = null;
|
|
3173
|
+
try {
|
|
3174
|
+
const m = JSON.parse(dec3.decode(kgit.readBlob(eng2, gitdir, commitOid, "meta") ?? new Uint8Array()));
|
|
3175
|
+
codec = m.codec ?? "gzip";
|
|
3176
|
+
stateRoot = m.stateRoot ?? null;
|
|
3177
|
+
} catch {
|
|
3178
|
+
}
|
|
3179
|
+
const dezip = async (b) => dec3.decode(codec === "gzip" ? await gunzip(b) : b);
|
|
3180
|
+
let blob = null;
|
|
3181
|
+
try {
|
|
3182
|
+
const b = kgit.readBlob(eng2, gitdir, commitOid, "checkpoint");
|
|
3183
|
+
if (b) blob = await dezip(b);
|
|
3184
|
+
} catch {
|
|
3185
|
+
}
|
|
3186
|
+
let frontier = null;
|
|
3187
|
+
try {
|
|
3188
|
+
const f = kgit.readBlob(eng2, gitdir, commitOid, "frontier");
|
|
3189
|
+
if (f) frontier = await dezip(f);
|
|
3190
|
+
} catch {
|
|
3191
|
+
}
|
|
3192
|
+
return { blob, frontier, stateRoot };
|
|
3193
|
+
}
|
|
3194
|
+
async function lawParity(eng2, ws, lawHash) {
|
|
3195
|
+
if (lawHash) return lawHash;
|
|
3196
|
+
try {
|
|
3197
|
+
const map = currentLaw(eng2, ws) || {};
|
|
3198
|
+
return await sha256hex(JSON.stringify(Object.entries(map).sort()));
|
|
3199
|
+
} catch {
|
|
3200
|
+
return null;
|
|
3201
|
+
}
|
|
3202
|
+
}
|
|
3203
|
+
async function pushCheckpoint(eng2, ws, ledger, lawHash) {
|
|
3204
|
+
try {
|
|
3205
|
+
lawHash = await lawParity(eng2, ws, lawHash) ?? "";
|
|
3206
|
+
if (!lawHash) return { pushed: false, reason: "no-law" };
|
|
3207
|
+
const gitdir = gitdirOf(ws);
|
|
3208
|
+
const head = kgit.resolveRef(eng2, gitdir, "refs/heads/main");
|
|
3209
|
+
eng2._ckptPushed ??= /* @__PURE__ */ new Map();
|
|
3210
|
+
if (head && eng2._ckptPushed.get(ws) === `${lawHash}:${head}`) {
|
|
3211
|
+
return { pushed: false, reason: "unchanged" };
|
|
3212
|
+
}
|
|
3213
|
+
const ex = exportCheckpoint(eng2, ws);
|
|
3214
|
+
if (!ex || ex.empty || !ex.ok || !ex.blob) return { pushed: false, reason: ex && ex.empty ? "empty" : "no-blob" };
|
|
3215
|
+
const tag = `${lawHash}:${ex.cursor}`;
|
|
3216
|
+
if (eng2._ckptPushed.get(ws) === tag) return { pushed: false, reason: "unchanged" };
|
|
3217
|
+
const { ref } = await writeCheckpointObjects(eng2, ws, ex, lawHash);
|
|
3218
|
+
await custodyPush(eng2, ws, ledger, { remoteRef: ref });
|
|
3219
|
+
eng2._ckptPushed.set(ws, tag);
|
|
3220
|
+
return { pushed: true, cursor: ex.cursor, bytes: ex.bytes ?? 0 };
|
|
3221
|
+
} catch (e) {
|
|
3222
|
+
return { pushed: false, reason: String(e?.message || e).slice(0, 160) };
|
|
3223
|
+
}
|
|
3224
|
+
}
|
|
3225
|
+
async function stageCheckpoint(eng2, ws, lawHash) {
|
|
3226
|
+
try {
|
|
3227
|
+
lawHash = await lawParity(eng2, ws, lawHash) ?? "";
|
|
3228
|
+
if (!lawHash) return null;
|
|
3229
|
+
eng2._ckptPushed ??= /* @__PURE__ */ new Map();
|
|
3230
|
+
const ex = exportCheckpoint(eng2, ws);
|
|
3231
|
+
if (!ex || ex.empty || !ex.ok || !ex.blob) return null;
|
|
3232
|
+
const tag = `${lawHash}:${ex.cursor}`;
|
|
3233
|
+
if (eng2._ckptPushed.get(ws) === tag) return null;
|
|
3234
|
+
const { ref } = await writeCheckpointObjects(eng2, ws, ex, lawHash);
|
|
3235
|
+
return { ref, tag, cursor: ex.cursor, bytes: ex.bytes ?? 0 };
|
|
3236
|
+
} catch {
|
|
3237
|
+
return null;
|
|
3238
|
+
}
|
|
3239
|
+
}
|
|
3240
|
+
async function pushStagedCheckpoint(eng2, ws, ledger, staged) {
|
|
3241
|
+
if (!staged || !staged.ref) return { pushed: false };
|
|
3242
|
+
await custodyPush(eng2, ws, ledger, { remoteRef: staged.ref });
|
|
3243
|
+
(eng2._ckptPushed ??= /* @__PURE__ */ new Map()).set(ws, staged.tag);
|
|
3244
|
+
return { pushed: true, cursor: staged.cursor, bytes: staged.bytes };
|
|
3245
|
+
}
|
|
3246
|
+
async function clearCheckpointRefs(ledger) {
|
|
3247
|
+
const headers = ledger.headers || {};
|
|
3248
|
+
const refs = await lsRefs(ledger.remote, headers, { service: "git-receive-pack", prefix: "refs/meta/checkpoint/" });
|
|
3249
|
+
const deleted = [];
|
|
3250
|
+
for (const ref of Object.keys(refs)) {
|
|
3251
|
+
try {
|
|
3252
|
+
await pushVia(ledger, () => pushPack(ledger.remote, headers, { updates: [{ ref, oldOid: refs[ref], newOid: ZERO_OID }] }));
|
|
3253
|
+
deleted.push(ref);
|
|
3254
|
+
} catch {
|
|
3255
|
+
}
|
|
3256
|
+
}
|
|
3257
|
+
return deleted;
|
|
3258
|
+
}
|
|
3259
|
+
async function restoreCheckpointFromCustody(eng2, ws, ledger, lawHash) {
|
|
3260
|
+
try {
|
|
3261
|
+
const mounted = eng2.mounted.get(ws);
|
|
3262
|
+
if (mounted?.checkpoint?.restored) {
|
|
3263
|
+
return { restored: false, reason: "already-mounted-checkpoint" };
|
|
3264
|
+
}
|
|
3265
|
+
lawHash = await lawParity(eng2, ws, lawHash) ?? "";
|
|
3266
|
+
if (!lawHash) return { restored: false, reason: "no-law" };
|
|
3267
|
+
const ref = CKPT_REF(lawHash);
|
|
3268
|
+
const serverRefs = await custodyLsRefs(ledger, ref);
|
|
3269
|
+
const foundOid = serverRefs[ref];
|
|
3270
|
+
if (!foundOid) return { restored: false, reason: "absent" };
|
|
3271
|
+
await custodyFetch(eng2, ws, ledger, { remoteRef: ref });
|
|
3272
|
+
const { blob, frontier, stateRoot } = await readCheckpointBlob(eng2, ws, foundOid);
|
|
3273
|
+
const r = importCheckpoint(eng2, ws, blob, frontier, stateRoot);
|
|
3274
|
+
if (r && r.restored === true && r.cursor) {
|
|
3275
|
+
eng2._ckptPushed ??= /* @__PURE__ */ new Map();
|
|
3276
|
+
eng2._ckptPushed.set(ws, `${lawHash}:${r.cursor}`);
|
|
3277
|
+
}
|
|
3278
|
+
return { restored: r && r.restored === true, frontierSeeded: r && r.frontierSeeded === true, cursor: r && r.cursor, reason: r && r.reason };
|
|
3279
|
+
} catch (e) {
|
|
3280
|
+
return { restored: false, reason: String(e?.message || e).slice(0, 160) };
|
|
3281
|
+
}
|
|
3282
|
+
}
|
|
3283
|
+
var pushVia = (ledger, fn) => ledger && ledger.pushLane ? ledger.pushLane(fn) : fn();
|
|
3284
|
+
var fullRef = (r) => r === "HEAD" || r.startsWith("refs/") ? r : `refs/heads/${r}`;
|
|
3285
|
+
var applyPackInto = (eng2, ws, pack) => {
|
|
3286
|
+
if (!pack || !pack.length) return;
|
|
3287
|
+
const r = JSON.parse(call(eng2.ex, "apply_pack", { repoArg: repoArgOf(ws), packB64: b64Bytes(pack) }, eng2.STDERR));
|
|
3288
|
+
if (!r || r.ok !== true) throw new Error(`apply_pack: ${r && r.error}`);
|
|
3289
|
+
};
|
|
3290
|
+
async function custodySkeleton(eng2, gitdir) {
|
|
3291
|
+
const p = eng2.fs.promises;
|
|
3292
|
+
for (const d of ["", "/objects", "/objects/pack", "/objects/info", "/refs", "/refs/heads"]) {
|
|
3293
|
+
try {
|
|
3294
|
+
await p.mkdir(`${gitdir}${d}`);
|
|
3295
|
+
} catch (e) {
|
|
3296
|
+
if (e?.code !== "EEXIST") throw e;
|
|
3297
|
+
}
|
|
3298
|
+
}
|
|
3299
|
+
try {
|
|
3300
|
+
await p.writeFile(`${gitdir}/HEAD`, `ref: refs/heads/${BRANCH}
|
|
3301
|
+
`);
|
|
3302
|
+
} catch {
|
|
3303
|
+
}
|
|
3304
|
+
try {
|
|
3305
|
+
await p.writeFile(`${gitdir}/config`, "[core]\n repositoryformatversion = 0\n bare = true\n");
|
|
3306
|
+
} catch {
|
|
3307
|
+
}
|
|
3308
|
+
}
|
|
3309
|
+
var custodyLsRefs = (ledger, prefix = null) => lsRefs(ledger.remote, ledger.headers || {}, { service: "git-upload-pack", prefix });
|
|
3310
|
+
async function custodyFetch(eng2, ws, ledger, { remoteRef = "refs/heads/main", localRef = null, have = null } = {}) {
|
|
3311
|
+
const remoteRefFull = fullRef(remoteRef);
|
|
3312
|
+
const refs = await custodyLsRefs(ledger, remoteRefFull);
|
|
3313
|
+
const tip = refs[remoteRefFull] || null;
|
|
3314
|
+
if (!tip) return null;
|
|
3315
|
+
if (tip !== have) {
|
|
3316
|
+
const pk = await fetchPack(ledger.remote, ledger.headers || {}, { wants: [tip], haves: have ? [have] : [] });
|
|
3317
|
+
if (pk) applyPackInto(eng2, ws, pk);
|
|
3318
|
+
}
|
|
3319
|
+
kgit.writeRef(eng2, gitdirOf(ws), fullRef(localRef || remoteRef), tip);
|
|
3320
|
+
return tip;
|
|
3321
|
+
}
|
|
3322
|
+
async function custodyPush(eng2, ws, ledger, { localRef = null, remoteRef, del = false, pointer = false, newOid = null }) {
|
|
3323
|
+
const headers = ledger.headers || {};
|
|
3324
|
+
const remoteRefFull = fullRef(remoteRef);
|
|
3325
|
+
const remote = await lsRefs(ledger.remote, headers, { service: "git-receive-pack", prefix: remoteRefFull });
|
|
3326
|
+
const oldOid = remote[remoteRefFull] || ZERO_OID;
|
|
3327
|
+
if (del) {
|
|
3328
|
+
if (oldOid === ZERO_OID) return;
|
|
3329
|
+
return pushVia(ledger, () => pushPack(ledger.remote, headers, { updates: [{ ref: remoteRefFull, oldOid, newOid: ZERO_OID }] }));
|
|
3330
|
+
}
|
|
3331
|
+
const tgt = newOid || kgit.resolveRef(eng2, gitdirOf(ws), fullRef(localRef || remoteRef));
|
|
3332
|
+
if (!tgt) throw new Error(`custodyPush: local ref ${localRef || remoteRef} unresolved`);
|
|
3333
|
+
if (tgt === oldOid) return;
|
|
3334
|
+
let pack = null;
|
|
3335
|
+
if (!pointer) {
|
|
3336
|
+
pack = oldOid !== ZERO_OID ? packDelta(eng2, ws, oldOid, tgt) : null;
|
|
3337
|
+
if (!pack) pack = packDelta(eng2, ws, "", tgt);
|
|
3338
|
+
}
|
|
3339
|
+
return pushVia(ledger, () => pushPack(ledger.remote, headers, { updates: [{ ref: remoteRefFull, oldOid, newOid: tgt }], pack }));
|
|
3340
|
+
}
|
|
3341
|
+
async function commitAndPush(eng2, ws, ledger) {
|
|
3342
|
+
try {
|
|
3343
|
+
await custodyPush(eng2, ws, ledger, { remoteRef: "refs/heads/main" });
|
|
3344
|
+
} catch (e) {
|
|
3345
|
+
unmount(eng2, ws);
|
|
3346
|
+
throw new Error(`ledger push failed \u2014 write NOT durable, rolled back to ledger: ${e}`);
|
|
3347
|
+
}
|
|
3348
|
+
}
|
|
3349
|
+
async function pushShape(eng2, ws, ledger) {
|
|
3350
|
+
try {
|
|
3351
|
+
const st = projectionStats(eng2, ws);
|
|
3352
|
+
const bytes = st && (st.sqliteBytes ?? st.bytes);
|
|
3353
|
+
if (bytes == null) return;
|
|
3354
|
+
const gitdir = gitdirOf(ws);
|
|
3355
|
+
const head = kgit.resolveRef(eng2, gitdir, "refs/heads/main");
|
|
3356
|
+
if (!head) return;
|
|
3357
|
+
const newRef = `refs/meta/shape/b${bytes}`;
|
|
3358
|
+
eng2._lastShapeRef ??= /* @__PURE__ */ new Map();
|
|
3359
|
+
if (eng2._lastShapeRef.get(ws) === newRef) return;
|
|
3360
|
+
kgit.writeRef(eng2, gitdir, newRef, head);
|
|
3361
|
+
await custodyPush(eng2, ws, ledger, { remoteRef: newRef, pointer: true });
|
|
3362
|
+
const prev = eng2._lastShapeRef.get(ws);
|
|
3363
|
+
if (prev && prev !== newRef) {
|
|
3364
|
+
try {
|
|
3365
|
+
await custodyPush(eng2, ws, ledger, { remoteRef: prev, del: true });
|
|
3366
|
+
} catch {
|
|
3367
|
+
}
|
|
3368
|
+
}
|
|
3369
|
+
eng2._lastShapeRef.set(ws, newRef);
|
|
3370
|
+
} catch {
|
|
3371
|
+
}
|
|
3372
|
+
}
|
|
3373
|
+
async function readShape(ledger) {
|
|
3374
|
+
try {
|
|
3375
|
+
const refs = await custodyLsRefs(ledger, "refs/meta/shape/");
|
|
3376
|
+
let best = null;
|
|
3377
|
+
for (const ref of Object.keys(refs)) {
|
|
3378
|
+
const m = /refs\/meta\/shape\/b(\d+)$/.exec(ref);
|
|
3379
|
+
if (m) {
|
|
3380
|
+
const bytes = +m[1];
|
|
3381
|
+
if (!best || bytes > best.bytes) best = { bytes };
|
|
3382
|
+
}
|
|
3383
|
+
}
|
|
3384
|
+
return best;
|
|
3385
|
+
} catch {
|
|
3386
|
+
return null;
|
|
3387
|
+
}
|
|
3388
|
+
}
|
|
3389
|
+
var qById = (eng2, ws, id, principal = "", keys = []) => JSON.parse(call(eng2.ex, "query", { repoArg: repoArgOf(ws), workspace: ws, queryBytes: b64Json({ op: "byId", aggregateId: id }), principal, keys, branch: BRANCH }, eng2.STDERR));
|
|
3390
|
+
var query = (eng2, ws, queryId, paramsJson, principal = "", keys = []) => JSON.parse(call(eng2.ex, "query", { repoArg: repoArgOf(ws), workspace: ws, queryBytes: b64Json({ kind: "query", queryId, paramsJson }), principal, keys, branch: BRANCH }, eng2.STDERR));
|
|
3391
|
+
var check = (eng2, ws, object, relation, subject) => JSON.parse(call(eng2.ex, "query", { repoArg: repoArgOf(ws), workspace: ws, queryBytes: b64Json({ op: "check", object, relation, subject }), branch: BRANCH }, eng2.STDERR));
|
|
3392
|
+
var expand = (eng2, ws, object, relation) => JSON.parse(call(eng2.ex, "query", { repoArg: repoArgOf(ws), workspace: ws, queryBytes: b64Json({ op: "expand", object, relation }), branch: BRANCH }, eng2.STDERR));
|
|
3393
|
+
var cryptoSealField = (eng2, { scopeKey, scope, epoch, aggSid, fieldSid, plaintext }) => JSON.parse(call(eng2.ex, "query", { queryBytes: b64Json({ op: "cryptoSealField", scopeKey, scope, epoch, aggSid, fieldSid, plaintext }) }, eng2.STDERR)).envelope;
|
|
3394
|
+
var cryptoOwnedScopeKey = (eng2, workspace2, authorSecret, scope, principal = "", requireSeat = true, record, recordEpoch = 0, recordWrap) => {
|
|
3395
|
+
const recordObject = record === void 0 ? void 0 : `${record.type}:${record.id}`;
|
|
3396
|
+
const derived = JSON.parse(call(eng2.ex, "query", {
|
|
3397
|
+
queryBytes: b64Json({
|
|
3398
|
+
op: "cryptoOwnedScopeKey",
|
|
3399
|
+
workspace: workspace2,
|
|
3400
|
+
principal,
|
|
3401
|
+
authorSecret,
|
|
3402
|
+
scope,
|
|
3403
|
+
...recordObject === void 0 ? {} : { recordObject, recordEpoch },
|
|
3404
|
+
...recordWrap === void 0 ? {} : { recordWrap }
|
|
3405
|
+
})
|
|
3406
|
+
}, eng2.STDERR));
|
|
3407
|
+
const ownedKeySubject = String(derived.ownedKeySubject || "");
|
|
3408
|
+
if (!requireSeat) {
|
|
3409
|
+
return {
|
|
3410
|
+
scope: String(derived.scope || scope),
|
|
3411
|
+
epoch: Number(derived.epoch) || 0,
|
|
3412
|
+
key: String(derived.scopeKey || ""),
|
|
3413
|
+
ownedKeySubject,
|
|
3414
|
+
ownedCustody: typeof derived.ownedCustody === "string" ? derived.ownedCustody : void 0,
|
|
3415
|
+
keyCommitment: typeof derived.keyCommitment === "string" ? derived.keyCommitment : void 0,
|
|
3416
|
+
principalWrappingPublic: typeof derived.principalWrappingPublic === "string" ? derived.principalWrappingPublic : void 0
|
|
3417
|
+
};
|
|
3418
|
+
}
|
|
3419
|
+
if (principal) {
|
|
3420
|
+
let hex = "";
|
|
3421
|
+
for (const byte of enc3.encode(principal)) hex += byte.toString(16).padStart(2, "0");
|
|
3422
|
+
const object = `nomos-owned-principal:${hex || "00"}`;
|
|
3423
|
+
const rows = qById(eng2, workspace2, `reltuple-${object}-nomos_internal_owned_device_key_7c1f5e`);
|
|
3424
|
+
const row = Array.isArray(rows) ? rows[0] : rows?.rows?.[0];
|
|
3425
|
+
const leaves = row?.data?.subjects ?? row?.subjects ?? {};
|
|
3426
|
+
const leaf = (raw) => {
|
|
3427
|
+
if (raw && typeof raw === "object") return raw;
|
|
3428
|
+
try {
|
|
3429
|
+
return typeof raw === "string" ? JSON.parse(raw) : {};
|
|
3430
|
+
} catch {
|
|
3431
|
+
return {};
|
|
3432
|
+
}
|
|
3433
|
+
};
|
|
3434
|
+
const active = Object.entries(leaves).filter(([, raw]) => leaf(raw).status === "active").map(([subject]) => subject);
|
|
3435
|
+
if (active.length === 0 && Object.keys(leaves).length === 0) {
|
|
3436
|
+
return {
|
|
3437
|
+
scope: String(derived.scope || scope),
|
|
3438
|
+
epoch: Number(derived.epoch) || 0,
|
|
3439
|
+
key: String(derived.scopeKey || ""),
|
|
3440
|
+
ownedKeySubject,
|
|
3441
|
+
ownedCustody: typeof derived.ownedCustody === "string" ? derived.ownedCustody : void 0,
|
|
3442
|
+
keyCommitment: typeof derived.keyCommitment === "string" ? derived.keyCommitment : void 0,
|
|
3443
|
+
principalWrappingPublic: typeof derived.principalWrappingPublic === "string" ? derived.principalWrappingPublic : void 0
|
|
3444
|
+
};
|
|
3445
|
+
}
|
|
3446
|
+
if (!active.includes(ownedKeySubject)) {
|
|
3447
|
+
throw new Error(
|
|
3448
|
+
"this device is waiting for approval \u2014 approve its opaque request on an already-working device"
|
|
3449
|
+
);
|
|
3450
|
+
}
|
|
3451
|
+
const custody = String(leaf(leaves[ownedKeySubject]).ownedCustody || "");
|
|
3452
|
+
if (!custody) throw new Error("the approved device has no owned-data handoff");
|
|
3453
|
+
const opened = JSON.parse(call(eng2.ex, "query", {
|
|
3454
|
+
queryBytes: b64Json({
|
|
3455
|
+
op: "cryptoOwnedScopeKey",
|
|
3456
|
+
workspace: workspace2,
|
|
3457
|
+
principal,
|
|
3458
|
+
authorSecret,
|
|
3459
|
+
scope,
|
|
3460
|
+
ownedCustody: custody,
|
|
3461
|
+
...recordObject === void 0 ? {} : { recordObject, recordEpoch },
|
|
3462
|
+
...recordWrap === void 0 ? {} : { recordWrap }
|
|
3463
|
+
})
|
|
3464
|
+
}, eng2.STDERR));
|
|
3465
|
+
return {
|
|
3466
|
+
scope: String(opened.scope || scope),
|
|
3467
|
+
epoch: Number(opened.epoch) || 0,
|
|
3468
|
+
key: String(opened.scopeKey || ""),
|
|
3469
|
+
ownedKeySubject,
|
|
3470
|
+
keyCommitment: typeof opened.keyCommitment === "string" ? opened.keyCommitment : void 0,
|
|
3471
|
+
principalWrappingPublic: typeof opened.principalWrappingPublic === "string" ? opened.principalWrappingPublic : void 0
|
|
3472
|
+
};
|
|
3473
|
+
}
|
|
3474
|
+
return {
|
|
3475
|
+
scope: String(derived.scope || scope),
|
|
3476
|
+
epoch: Number(derived.epoch) || 0,
|
|
3477
|
+
key: String(derived.scopeKey || ""),
|
|
3478
|
+
ownedKeySubject,
|
|
3479
|
+
ownedCustody: typeof derived.ownedCustody === "string" ? derived.ownedCustody : void 0,
|
|
3480
|
+
keyCommitment: typeof derived.keyCommitment === "string" ? derived.keyCommitment : void 0,
|
|
3481
|
+
principalWrappingPublic: typeof derived.principalWrappingPublic === "string" ? derived.principalWrappingPublic : void 0
|
|
3482
|
+
};
|
|
3483
|
+
};
|
|
3484
|
+
var cryptoOwnedDeviceRequest = (eng2, workspace2, principal, authorSecret, scope) => String(JSON.parse(call(eng2.ex, "query", {
|
|
3485
|
+
queryBytes: b64Json({ op: "cryptoOwnedDeviceRequest", workspace: workspace2, principal, authorSecret, scope })
|
|
3486
|
+
}, eng2.STDERR)).request || "");
|
|
3487
|
+
var cryptoOwnedPrepareApproval = (eng2, args) => JSON.parse(call(eng2.ex, "query", {
|
|
3488
|
+
queryBytes: b64Json({ op: "cryptoOwnedPrepareApproval", ...args })
|
|
3489
|
+
}, eng2.STDERR));
|
|
3490
|
+
var cryptoOwnedPrepareRecovery = (eng2, args) => JSON.parse(call(eng2.ex, "query", {
|
|
3491
|
+
queryBytes: b64Json({ op: "cryptoOwnedPrepareRecovery", ...args })
|
|
3492
|
+
}, eng2.STDERR));
|
|
3493
|
+
var cryptoKeygen = (eng2) => JSON.parse(call(eng2.ex, "query", { queryBytes: b64Json({ op: "cryptoKeygen" }) }, eng2.STDERR));
|
|
3494
|
+
var authorKeygen = (eng2) => JSON.parse(call(eng2.ex, "query", { queryBytes: b64Json({ op: "authorKeygen" }) }, eng2.STDERR));
|
|
3495
|
+
var eraCertSign = (eng2, args) => JSON.parse(call(eng2.ex, "query", { queryBytes: b64Json({ op: "eraCertSign", ...args }) }, eng2.STDERR));
|
|
3496
|
+
var delegationCertSign = (eng2, { secret, grantorId, relation, subject, epoch, certChain }) => JSON.parse(call(eng2.ex, "query", { queryBytes: b64Json({ op: "delegationCertSign", secret, grantorId, relation, subject, epoch, ...certChain ? { certChain } : {} }) }, eng2.STDERR));
|
|
3497
|
+
function describe(eng2, ws) {
|
|
3498
|
+
return JSON.parse(call(eng2.ex, "query", { repoArg: repoArgOf(ws), workspace: ws, queryBytes: b64Json({ op: "describe", workspace: ws }), branch: BRANCH }, eng2.STDERR));
|
|
3499
|
+
}
|
|
3500
|
+
var attestationSign = (eng2, { secret, asserterWorkspace, object, relation, subject, attestedAt }) => JSON.parse(call(eng2.ex, "query", { queryBytes: b64Json({ op: "attestationSign", secret, asserterWorkspace, object, relation, subject, attestedAt }) }, eng2.STDERR));
|
|
3501
|
+
var attestedRead = (eng2, ws, { queryId, paramsJson, attestorSecret, nowMs, includeRosterProof }) => JSON.parse(call(eng2.ex, "query", {
|
|
3502
|
+
repoArg: repoArgOf(ws),
|
|
3503
|
+
workspace: ws,
|
|
3504
|
+
branch: BRANCH,
|
|
3505
|
+
queryBytes: b64Json({ op: "attestedRead", queryId, paramsJson: paramsJson || "{}", attestorSecret, nowMs: nowMs ?? Date.now(), sourceWorkspace: ws, ...includeRosterProof ? { includeRosterProof: true } : {} })
|
|
3506
|
+
}, eng2.STDERR));
|
|
3507
|
+
function parseAttestedReadNeed(error) {
|
|
3508
|
+
const m = /attested-read-unreachable: read\('([^']+)', \{from: '([^']+)'\}\) args=(.*?) has no injected attestation/.exec(String(error || ""));
|
|
3509
|
+
if (!m) return null;
|
|
3510
|
+
let blob = m[3];
|
|
3511
|
+
for (let i = 0; i < 4; i++) {
|
|
3512
|
+
try {
|
|
3513
|
+
return { queryId: m[1], from: m[2], args: JSON.parse(blob) };
|
|
3514
|
+
} catch {
|
|
3515
|
+
blob = blob.replace(/\\(["\\])/g, "$1");
|
|
3516
|
+
}
|
|
3517
|
+
}
|
|
3518
|
+
return null;
|
|
3519
|
+
}
|
|
3520
|
+
var certSign = (eng2, {
|
|
3521
|
+
child,
|
|
3522
|
+
founder,
|
|
3523
|
+
verified,
|
|
3524
|
+
parentKey,
|
|
3525
|
+
parentId,
|
|
3526
|
+
epoch,
|
|
3527
|
+
secret,
|
|
3528
|
+
verifiedVia,
|
|
3529
|
+
idpToken,
|
|
3530
|
+
capturedClock,
|
|
3531
|
+
authProviderIssuer,
|
|
3532
|
+
authProviderJwks,
|
|
3533
|
+
authProviderAudience
|
|
3534
|
+
}) => JSON.parse(call(eng2.ex, "query", { queryBytes: b64Json({
|
|
3535
|
+
op: "certSign",
|
|
3536
|
+
child,
|
|
3537
|
+
founder,
|
|
3538
|
+
verified,
|
|
3539
|
+
parentKey,
|
|
3540
|
+
parentId,
|
|
3541
|
+
epoch,
|
|
3542
|
+
secret,
|
|
3543
|
+
// forwarded only when present (the op reads them optionally; absent ⇒ classic cert bytes).
|
|
3544
|
+
...verifiedVia !== void 0 ? { verifiedVia } : {},
|
|
3545
|
+
...idpToken !== void 0 ? { idpToken } : {},
|
|
3546
|
+
...capturedClock !== void 0 ? { capturedClock } : {},
|
|
3547
|
+
...authProviderIssuer !== void 0 ? { authProviderIssuer } : {},
|
|
3548
|
+
...authProviderJwks !== void 0 ? { authProviderJwks } : {},
|
|
3549
|
+
...authProviderAudience !== void 0 ? { authProviderAudience } : {}
|
|
3550
|
+
}) }, eng2.STDERR)).cert;
|
|
3551
|
+
var cryptoWrapKey = (eng2, { recipientPub, scopeKey }) => JSON.parse(call(eng2.ex, "query", { queryBytes: b64Json({ op: "cryptoWrapKey", recipientPub, scopeKey }) }, eng2.STDERR));
|
|
3552
|
+
var cryptoUnwrapKey = (eng2, { secret, hpkeEpk, ct }) => JSON.parse(call(eng2.ex, "query", { queryBytes: b64Json({ op: "cryptoUnwrapKey", secret, hpkeEpk, ct }) }, eng2.STDERR)).scopeKey;
|
|
3553
|
+
var readPosture = (eng2, ws, principal = "", authToken = "", nowUnix = 0) => JSON.parse(call(eng2.ex, "query", { repoArg: repoArgOf(ws), workspace: ws, queryBytes: b64Json({ op: "readPosture", authToken, nowUnix }), principal, branch: BRANCH }, eng2.STDERR));
|
|
3554
|
+
var count = (eng2, ws, countId, groupKey, principal = "") => JSON.parse(call(eng2.ex, "query", { repoArg: repoArgOf(ws), workspace: ws, queryBytes: b64Json({ op: "count", countId, groupKey }), principal, branch: BRANCH }, eng2.STDERR));
|
|
3555
|
+
var sum = (eng2, ws, sumId, groupKey, principal = "") => JSON.parse(call(eng2.ex, "query", { repoArg: repoArgOf(ws), workspace: ws, queryBytes: b64Json({ op: "sum", sumId, groupKey }), principal, branch: BRANCH }, eng2.STDERR));
|
|
3556
|
+
var pendingConflicts = (eng2, ws, principal = "", aggregateType = "") => JSON.parse(call(eng2.ex, "query", { repoArg: repoArgOf(ws), workspace: ws, queryBytes: b64Json({ op: "pendingConflicts", aggregateType }), principal, branch: BRANCH }, eng2.STDERR));
|
|
3557
|
+
var pendingConflictSummary = (eng2, ws, principal = "") => JSON.parse(call(eng2.ex, "query", { repoArg: repoArgOf(ws), workspace: ws, queryBytes: b64Json({ op: "pendingConflictSummary" }), principal, branch: BRANCH }, eng2.STDERR));
|
|
3558
|
+
var spatialBboxParams = (bbox) => {
|
|
3559
|
+
const minX = bbox.minX ?? bbox.minLng, minY = bbox.minY ?? bbox.minLat, maxX = bbox.maxX ?? bbox.maxLng, maxY = bbox.maxY ?? bbox.maxLat;
|
|
3560
|
+
return { minX, minY, maxX, maxY, minLng: minX, minLat: minY, maxLng: maxX, maxLat: maxY };
|
|
3561
|
+
};
|
|
3562
|
+
var spatialWithin = (eng2, ws, spatialId, bbox, principal = "") => JSON.parse(call(eng2.ex, "query", { repoArg: repoArgOf(ws), workspace: ws, queryBytes: b64Json({ op: "spatial", spatialId, ...spatialBboxParams(bbox) }), principal, branch: BRANCH }, eng2.STDERR));
|
|
3563
|
+
var extremum = (eng2, ws, extremumId, kind, groupKey, principal = "") => JSON.parse(call(eng2.ex, "query", { repoArg: repoArgOf(ws), workspace: ws, queryBytes: b64Json({ op: "extremum", extremumId, kind, groupKey: groupKey ?? "" }), principal, branch: BRANCH }, eng2.STDERR));
|
|
3564
|
+
function packDelta(eng2, ws, base, head) {
|
|
3565
|
+
try {
|
|
3566
|
+
const r = JSON.parse(call(eng2.ex, "pack_delta", { repoArg: repoArgOf(ws), base, head }, eng2.STDERR));
|
|
3567
|
+
if (!r || r.ok !== true || typeof r.packB64 !== "string") return null;
|
|
3568
|
+
return Uint8Array.from(atob(r.packB64), (c) => c.charCodeAt(0));
|
|
3569
|
+
} catch {
|
|
3570
|
+
return null;
|
|
3571
|
+
}
|
|
3572
|
+
}
|
|
3573
|
+
function servePack(eng2, ws, base = "") {
|
|
3574
|
+
let head = null;
|
|
3575
|
+
try {
|
|
3576
|
+
head = kgit.resolveRef(eng2, gitdirOf(ws), BRANCH);
|
|
3577
|
+
} catch {
|
|
3578
|
+
}
|
|
3579
|
+
let r;
|
|
3580
|
+
try {
|
|
3581
|
+
r = JSON.parse(call(eng2.ex, "pack_delta", { repoArg: repoArgOf(ws), workspace: ws, base, branch: BRANCH }, eng2.STDERR));
|
|
3582
|
+
} catch (e) {
|
|
3583
|
+
const msg = String(e);
|
|
3584
|
+
if (/nothing to pack|head == base/.test(msg)) return { packB64: "", bytes: 0, upToDate: true, ...head ? { head } : {} };
|
|
3585
|
+
if (base && /reached root|before base|not found|unknown|no (?:merge )?base|missing|invalid/i.test(msg)) {
|
|
3586
|
+
try {
|
|
3587
|
+
const full = JSON.parse(call(eng2.ex, "pack_delta", { repoArg: repoArgOf(ws), workspace: ws, base: "", branch: BRANCH }, eng2.STDERR));
|
|
3588
|
+
if (full && full.ok === true && typeof full.packB64 === "string") {
|
|
3589
|
+
return { packB64: full.packB64, bytes: full.bytes, divergent: true, ...head ? { head } : {} };
|
|
3590
|
+
}
|
|
3591
|
+
} catch {
|
|
3592
|
+
}
|
|
3593
|
+
}
|
|
3594
|
+
throw e;
|
|
3595
|
+
}
|
|
3596
|
+
if (!r || r.ok !== true || typeof r.packB64 !== "string") throw new Error("servePack: kernel produced no pack");
|
|
3597
|
+
return { packB64: r.packB64, bytes: r.bytes, ...head ? { head } : {} };
|
|
3598
|
+
}
|
|
3599
|
+
function packBornChildren(eng2, born) {
|
|
3600
|
+
if (!Array.isArray(born) || !born.length) return born;
|
|
3601
|
+
return born.map((b) => {
|
|
3602
|
+
const child = b && typeof b.workspace === "string" ? b.workspace : null;
|
|
3603
|
+
const head = b && typeof b.head === "string" ? b.head : null;
|
|
3604
|
+
if (!child || !/^[0-9a-f]{40}$/i.test(head || "")) {
|
|
3605
|
+
log("born-pack-skipped", { workspace: child, head, reason: "malformed born entry" });
|
|
3606
|
+
return b;
|
|
3607
|
+
}
|
|
3608
|
+
try {
|
|
3609
|
+
const pack = packDelta(eng2, child, "", head);
|
|
3610
|
+
if (!pack || !pack.length) {
|
|
3611
|
+
log("born-pack-empty", { workspace: child, head });
|
|
3612
|
+
return b;
|
|
3613
|
+
}
|
|
3614
|
+
return { ...b, packB64: b64Bytes(pack), packBytes: pack.length };
|
|
3615
|
+
} catch (e) {
|
|
3616
|
+
log("born-pack-failed", { workspace: child, head, error: String(e).slice(0, 200) });
|
|
3617
|
+
return b;
|
|
3618
|
+
}
|
|
3619
|
+
});
|
|
3620
|
+
}
|
|
3621
|
+
function reconcileBornPacks(eng2, children) {
|
|
3622
|
+
const born = [];
|
|
3623
|
+
const missing = [];
|
|
3624
|
+
for (const child of Array.isArray(children) ? children : []) {
|
|
3625
|
+
if (typeof child !== "string" || !child) continue;
|
|
3626
|
+
const head = kgit.resolveRef(eng2, gitdirOf(child), BRANCH);
|
|
3627
|
+
if (!head || !/^[0-9a-f]{40}$/i.test(head)) {
|
|
3628
|
+
missing.push(child);
|
|
3629
|
+
continue;
|
|
3630
|
+
}
|
|
3631
|
+
const pack = packDelta(eng2, child, "", head);
|
|
3632
|
+
if (!pack || !pack.length) {
|
|
3633
|
+
missing.push(child);
|
|
3634
|
+
continue;
|
|
3635
|
+
}
|
|
3636
|
+
born.push({ workspace: child, head, packB64: b64Bytes(pack), packBytes: pack.length });
|
|
3637
|
+
}
|
|
3638
|
+
return { born, missing };
|
|
3639
|
+
}
|
|
3640
|
+
async function ingestPackAsCustody(eng2, ws, packBytes, head) {
|
|
3641
|
+
if (!packBytes || !packBytes.length) throw new Error(`ingestPackAsCustody: empty pack for '${ws}'`);
|
|
3642
|
+
if (!/^[0-9a-f]{40}$/i.test(head || "")) throw new Error(`ingestPackAsCustody: bad head '${head}' for '${ws}'`);
|
|
3643
|
+
unmount(eng2, ws);
|
|
3644
|
+
stageWorkspaceDir(eng2, ws);
|
|
3645
|
+
const gitdir = gitdirOf(ws);
|
|
3646
|
+
await custodySkeleton(eng2, gitdir);
|
|
3647
|
+
applyPackInto(eng2, ws, packBytes);
|
|
3648
|
+
kgit.writeRef(eng2, gitdir, "refs/heads/main", head);
|
|
3649
|
+
kgit.writeRef(eng2, gitdir, "HEAD", "ref: refs/heads/main");
|
|
3650
|
+
eng2.mounted.set(ws, { restored: true, remoteMain: head, restoreError: null });
|
|
3651
|
+
return { ok: true, head: kgit.resolveRef(eng2, gitdir, "refs/heads/main") };
|
|
3652
|
+
}
|
|
3653
|
+
var nomosActive = (eng2, ws) => qById(eng2, ws, `domain-installation:${eng2.hashes.nomos}`).length > 0;
|
|
3654
|
+
function offerIntent(eng2, ws, bytes, opts = {}) {
|
|
3655
|
+
const name = `offer-in-${eng2.seq++}.json`;
|
|
3656
|
+
writeWork(eng2, name, bytes);
|
|
3657
|
+
const deferProjection = !!(opts && opts.defer);
|
|
3658
|
+
const branch = opts && opts.branch || BRANCH;
|
|
3659
|
+
return JSON.parse(call(eng2.ex, "offer", { repoArg: repoArgOf(ws), workspace: ws, intentFile: `/work/${name}`, branch, deferProjection }, eng2.STDERR));
|
|
3660
|
+
}
|
|
3661
|
+
function offerAdmit(eng2, ws, bytes, opts = {}) {
|
|
3662
|
+
const v = offerIntent(eng2, ws, bytes, opts);
|
|
3663
|
+
return v.outcome === "admitted" ? { ok: true, head: v.head, ...v.born ? { born: v.born } : {} } : { ok: false, error: v.verdict?.reason ?? v.error ?? "the offer was refused" };
|
|
3664
|
+
}
|
|
3665
|
+
function applyIntentBytes(eng2, ws, bytes, opts = {}) {
|
|
3666
|
+
return offerAdmit(eng2, ws, bytes, opts);
|
|
3667
|
+
}
|
|
3668
|
+
async function mainIntentIds(eng2, ws) {
|
|
3669
|
+
const gitdir = gitdirOf(ws);
|
|
3670
|
+
const head = kgit.resolveRef(eng2, gitdir, BRANCH);
|
|
3671
|
+
if (!head) return { head: null, ids: /* @__PURE__ */ new Set(), oids: /* @__PURE__ */ new Set() };
|
|
3672
|
+
const cached = eng2.mainIntents.get(ws);
|
|
3673
|
+
if (cached && cached.head === head) return cached;
|
|
3674
|
+
const ids = /* @__PURE__ */ new Set(), oids = /* @__PURE__ */ new Set();
|
|
3675
|
+
for (const c of kgit.intentsAbove(eng2, gitdir, head, "", 500)) {
|
|
3676
|
+
oids.add(c.oid);
|
|
3677
|
+
try {
|
|
3678
|
+
const id = JSON.parse(dec3.decode(bytesFromB64(c.intentB64))).id;
|
|
3679
|
+
if (typeof id === "string" && id) ids.add(id);
|
|
3680
|
+
} catch {
|
|
3681
|
+
}
|
|
3682
|
+
}
|
|
3683
|
+
const m = { head, ids, oids };
|
|
3684
|
+
eng2.mainIntents.set(ws, m);
|
|
3685
|
+
return m;
|
|
3686
|
+
}
|
|
3687
|
+
async function admitAll(eng2, ws, ledger, { receiptFence = null, shardRouting = null, defer = false } = {}) {
|
|
3688
|
+
const routes = shardRouting ? directiveRoutes(eng2) : null;
|
|
3689
|
+
const gitdir = gitdirOf(ws);
|
|
3690
|
+
const H = () => ({ ...ledger.headers || {} });
|
|
3691
|
+
const _t0 = Date.now();
|
|
3692
|
+
let _fetchMs = 0, _sealMs = 0, _intents = 0;
|
|
3693
|
+
const _spans = [];
|
|
3694
|
+
let _traceSeq = 0;
|
|
3695
|
+
const _span = (name, kind, depth = 1, extra = {}) => {
|
|
3696
|
+
const t0 = Date.now();
|
|
3697
|
+
return (end = {}) => {
|
|
3698
|
+
const t1 = Date.now();
|
|
3699
|
+
_spans.push({ id: `admit-${_t0}-${_traceSeq++}`, track: "cloud", name, kind, depth, t0, t1, ...extra, ...end });
|
|
3700
|
+
};
|
|
3701
|
+
};
|
|
3702
|
+
const sessionRefPrefix = "refs/heads/session/";
|
|
3703
|
+
const sessions = {};
|
|
3704
|
+
const _scanDone = _span("scan session refs", "cscan", 1);
|
|
3705
|
+
for (const [ref, oid] of Object.entries(await custodyLsRefs(ledger, sessionRefPrefix))) {
|
|
3706
|
+
if (ref.startsWith(sessionRefPrefix) && oid) sessions[ref.slice(sessionRefPrefix.length)] = oid;
|
|
3707
|
+
}
|
|
3708
|
+
_scanDone({ sessions: Object.keys(sessions).length });
|
|
3709
|
+
const _scanMs = Date.now() - _t0;
|
|
3710
|
+
const results = [], deadLetters = [], placements = [], splits = [], globalsOut = [];
|
|
3711
|
+
const consumed = [];
|
|
3712
|
+
let totalAdmitted = 0;
|
|
3713
|
+
const globalsDeclared = shardRouting && !shardRouting.selfLabel ? globalAggregates(eng2) : null;
|
|
3714
|
+
const summaryCtx = shardRouting && shardRouting.selfLabel && scopedReads(eng2).length > 0 ? { reads: scopedReads(eng2), prevs: /* @__PURE__ */ new Map(), values: /* @__PURE__ */ new Map(), events: [], rowsDelta: 0, rederived: /* @__PURE__ */ new Set(), rowsByHome: /* @__PURE__ */ new Map(), homeTags: /* @__PURE__ */ new Map(), homeKeySet: /* @__PURE__ */ new Set() } : null;
|
|
3715
|
+
const canDeferProjection = !summaryCtx && !(globalsDeclared && globalsDeclared.size > 0);
|
|
3716
|
+
if (summaryCtx) {
|
|
3717
|
+
try {
|
|
3718
|
+
for (const r of query(eng2, ws, "aggregatesByType", JSON.stringify({ __type: "NomosHomeReceipt" }))) {
|
|
3719
|
+
const hk = r.data && typeof r.data.homeKey === "string" ? r.data.homeKey : null;
|
|
3720
|
+
if (!hk) continue;
|
|
3721
|
+
summaryCtx.homeKeySet.add(hk);
|
|
3722
|
+
summaryCtx.homeTags.set(await routeTagHexOfHomeKey(hk), hk);
|
|
3723
|
+
}
|
|
3724
|
+
} catch (e) {
|
|
3725
|
+
log("home-tag-table-error", { ws, error: String(e).slice(0, 200) });
|
|
3726
|
+
}
|
|
3727
|
+
}
|
|
3728
|
+
const headBefore = summaryCtx ? kgit.resolveRef(eng2, gitdir, BRANCH) : null;
|
|
3729
|
+
for (const [cid, head] of Object.entries(sessions)) {
|
|
3730
|
+
const ref = `refs/heads/session/${cid}`;
|
|
3731
|
+
const shortCid = cid.length > 14 ? `${cid.slice(0, 14)}...` : cid;
|
|
3732
|
+
const _sessionDone = _span(`session ${shortCid}`, "csession", 1, { client: cid });
|
|
3733
|
+
try {
|
|
3734
|
+
const _fetchDone = _span(`fetch ${shortCid}`, "cfetch", 2, { client: cid });
|
|
3735
|
+
const _f = Date.now();
|
|
3736
|
+
try {
|
|
3737
|
+
const localMain = kgit.resolveRef(eng2, gitdir, BRANCH);
|
|
3738
|
+
try {
|
|
3739
|
+
await custodyFetch(eng2, ws, ledger, { remoteRef: ref, have: localMain });
|
|
3740
|
+
} catch (thinErr) {
|
|
3741
|
+
log("thin-fetch-fallback", { ws, cid, error: String(thinErr?.message || thinErr).slice(0, 160) });
|
|
3742
|
+
await custodyFetch(eng2, ws, ledger, { remoteRef: ref, have: null });
|
|
3743
|
+
}
|
|
3744
|
+
} finally {
|
|
3745
|
+
_fetchMs += Date.now() - _f;
|
|
3746
|
+
_fetchDone();
|
|
3747
|
+
}
|
|
3748
|
+
const _walkDone = _span(`walk ${shortCid}`, "cwalk", 2, { client: cid });
|
|
3749
|
+
const main = await mainIntentIds(eng2, ws);
|
|
3750
|
+
const recent = kgit.intentsAbove(eng2, gitdir, head ?? "", "", 100);
|
|
3751
|
+
const chain = [];
|
|
3752
|
+
for (let i = recent.length - 1; i >= 0; i--) {
|
|
3753
|
+
if (main.oids.has(recent[i].oid)) break;
|
|
3754
|
+
chain.push(recent[i]);
|
|
3755
|
+
}
|
|
3756
|
+
chain.reverse();
|
|
3757
|
+
_walkDone({ intents: chain.length });
|
|
3758
|
+
const admitted = [], rejected = [], skipped = [];
|
|
3759
|
+
const _gateDone = _span(`gate+fold ${shortCid}`, "cgate", 2, { client: cid, intents: chain.length });
|
|
3760
|
+
for (const c of chain) {
|
|
3761
|
+
const oid = c.oid;
|
|
3762
|
+
try {
|
|
3763
|
+
const blob = bytesFromB64(c.intentB64);
|
|
3764
|
+
let dom = null, intentId = null, dirId = null;
|
|
3765
|
+
try {
|
|
3766
|
+
const doc = JSON.parse(dec3.decode(blob));
|
|
3767
|
+
intentId = typeof doc.id === "string" ? doc.id : null;
|
|
3768
|
+
dom = doc && doc.payload && typeof doc.payload.domain === "string" ? doc.payload.domain : null;
|
|
3769
|
+
dirId = doc && doc.payload && typeof doc.payload.directiveId === "string" ? doc.payload.directiveId : null;
|
|
3770
|
+
} catch {
|
|
3771
|
+
}
|
|
3772
|
+
if (!dom) {
|
|
3773
|
+
rejected.push({ oid: oid.slice(0, 10), error: "non-authored intent \u2014 governance enters its own admin lane" });
|
|
3774
|
+
continue;
|
|
3775
|
+
}
|
|
3776
|
+
if (receiptFence && dirId && Array.isArray(receiptFence[dom]) && receiptFence[dom].includes(dirId)) {
|
|
3777
|
+
const error = `'${dom}/${dirId}' is a capability RECEIPT \u2014 receipts are executor/owner writes, never open-session writes. Author it via POST /v1/workspaces/${ws}/author (owner Bearer), or \u2014 if this parked receipt is sanctioned \u2014 the owner unjams it: POST /v1/workspaces/${ws}/dead-letters/retry`;
|
|
3778
|
+
rejected.push({ oid: oid.slice(0, 10), error, deadLettered: true });
|
|
3779
|
+
deadLetters.push({ intentId, oid: oid.slice(0, 10), session: cid, error, blob, domain: dom, directiveId: dirId });
|
|
3780
|
+
continue;
|
|
3781
|
+
}
|
|
3782
|
+
if (routes && dom && dirId) {
|
|
3783
|
+
if (dirId === "nomosSealHome" || dirId === "nomosSealHandoff") {
|
|
3784
|
+
rejected.push({ oid: oid.slice(0, 10), error: `session lane refuses '${dirId}' \u2014 the \xA76 handoff seals are authored by the adapter after migration verifies (begin a move with nomosSplitShard, or POST /v1/workspaces/<coordinator>/split)` });
|
|
3785
|
+
continue;
|
|
3786
|
+
}
|
|
3787
|
+
const route = routes.get(`${dom} ${dirId}`);
|
|
3788
|
+
if (route) {
|
|
3789
|
+
let payloadObj = null;
|
|
3790
|
+
try {
|
|
3791
|
+
payloadObj = JSON.parse(dec3.decode(blob)).payload?.payload ?? null;
|
|
3792
|
+
} catch {
|
|
3793
|
+
}
|
|
3794
|
+
const verdict2 = await wrongHomeVerdict(shardRouting, route, payloadObj);
|
|
3795
|
+
if (verdict2) {
|
|
3796
|
+
rejected.push({ oid: oid.slice(0, 10), error: verdict2.error, ...verdict2.wrongHome ? { wrongHome: verdict2.wrongHome } : {}, ...verdict2.homeMoving ? { homeMoving: verdict2.homeMoving } : {}, ...verdict2.deadLettered ? { deadLettered: true } : {} });
|
|
3797
|
+
if (verdict2.deadLettered) deadLetters.push({ intentId, oid: oid.slice(0, 10), session: cid, error: verdict2.error, blob, domain: dom, directiveId: dirId });
|
|
3798
|
+
continue;
|
|
3799
|
+
}
|
|
3800
|
+
}
|
|
3801
|
+
if (dirId === "nomosReceiveAssignment" && shardRouting) {
|
|
3802
|
+
let p = null;
|
|
3803
|
+
try {
|
|
3804
|
+
p = JSON.parse(dec3.decode(blob)).payload?.payload ?? null;
|
|
3805
|
+
} catch {
|
|
3806
|
+
}
|
|
3807
|
+
const homeKey = p && typeof p.homeKey === "string" ? p.homeKey : null;
|
|
3808
|
+
const rows = homeKey ? await shardRouting.assignments() || [] : [];
|
|
3809
|
+
const a = homeKey ? rows.find((x) => x.homeKey === homeKey) : null;
|
|
3810
|
+
if (!a) {
|
|
3811
|
+
const error = `assignment receipt for home '${String(homeKey).slice(0, 80)}' has NO placement on the coordinator '${shardRouting.coordinatorWs}' \u2014 author the derived placement intent (birth<Type>) first, then POST /dead-letters/retry`;
|
|
3812
|
+
rejected.push({ oid: oid.slice(0, 10), error, deadLettered: true });
|
|
3813
|
+
deadLetters.push({ intentId, oid: oid.slice(0, 10), session: cid, error, blob, domain: dom, directiveId: dirId });
|
|
3814
|
+
continue;
|
|
3815
|
+
}
|
|
3816
|
+
if (a.shard !== shardRouting.selfLabel || p && p.shard !== shardRouting.selfLabel) {
|
|
3817
|
+
const correct = `${shardRouting.coordinatorWs}--${a.shard}`;
|
|
3818
|
+
rejected.push({
|
|
3819
|
+
oid: oid.slice(0, 10),
|
|
3820
|
+
error: `wrong-home: the coordinator's shard map assigns home '${homeKey.slice(0, 80)}' to '${correct}' (not this workspace) \u2014 a routed client re-routes and retries automatically`,
|
|
3821
|
+
wrongHome: { workspace: correct, ...a.mapVersion !== void 0 ? { mapVersion: a.mapVersion } : {} }
|
|
3822
|
+
});
|
|
3823
|
+
continue;
|
|
3824
|
+
}
|
|
3825
|
+
}
|
|
3826
|
+
}
|
|
3827
|
+
if (intentId && main.ids.has(intentId)) {
|
|
3828
|
+
skipped.push({ oid: oid.slice(0, 10), intent: intentId.slice(0, 12), alreadyOnMain: true });
|
|
3829
|
+
continue;
|
|
3830
|
+
}
|
|
3831
|
+
const cap = summaryCtx ? beginSummaryCapture(eng2, ws, summaryCtx, blob) : null;
|
|
3832
|
+
const verdict = offerIntent(eng2, ws, blob, { defer: canDeferProjection });
|
|
3833
|
+
const res = verdict.outcome === "admitted" ? { ok: true, head: verdict.head } : { ok: false, error: verdict.verdict?.reason ?? verdict.error ?? "the offer was refused" };
|
|
3834
|
+
if (res.ok) {
|
|
3835
|
+
main.ids.add(intentId);
|
|
3836
|
+
main.oids.add(res.head);
|
|
3837
|
+
main.head = res.head;
|
|
3838
|
+
admitted.push({ oid: oid.slice(0, 10), head: res.head });
|
|
3839
|
+
if (cap) finishSummaryCapture(eng2, ws, summaryCtx, cap, res.head ?? "", intentId);
|
|
3840
|
+
if (shardRouting && dirId) {
|
|
3841
|
+
const placement = placementDirectives(eng2).get(dirId);
|
|
3842
|
+
if (placement) {
|
|
3843
|
+
let p = null;
|
|
3844
|
+
try {
|
|
3845
|
+
p = JSON.parse(dec3.decode(blob)).payload?.payload ?? null;
|
|
3846
|
+
} catch {
|
|
3847
|
+
}
|
|
3848
|
+
const homeKey = p ? p[String(placement.keyField)] : void 0;
|
|
3849
|
+
if (typeof homeKey === "string" && typeof p?.shard === "string") {
|
|
3850
|
+
placements.push({ directive: dirId, homeKey, shard: p.shard, mapVersion: 1 });
|
|
3851
|
+
}
|
|
3852
|
+
}
|
|
3853
|
+
if (dirId === "nomosSplitShard" && !shardRouting.selfLabel) {
|
|
3854
|
+
let p = null;
|
|
3855
|
+
try {
|
|
3856
|
+
p = JSON.parse(dec3.decode(blob)).payload?.payload ?? null;
|
|
3857
|
+
} catch {
|
|
3858
|
+
}
|
|
3859
|
+
if (p && typeof p.fromShard === "string" && typeof p.toShard === "string" && Array.isArray(p.homeKeys)) {
|
|
3860
|
+
splits.push({ fromShard: p.fromShard, toShard: p.toShard, homeKeys: p.homeKeys.filter((k) => typeof k === "string") });
|
|
3861
|
+
}
|
|
3862
|
+
}
|
|
3863
|
+
}
|
|
3864
|
+
if (globalsDeclared && globalsDeclared.size > 0) {
|
|
3865
|
+
let touchesGlobal = false;
|
|
3866
|
+
for (const ev of parseIntentEvents(blob)) {
|
|
3867
|
+
const rows = qById(eng2, ws, ev.aggregate);
|
|
3868
|
+
const t = ev.sets.get("__type") ?? (rows.length ? rows[0].type : void 0);
|
|
3869
|
+
if (t !== void 0 && globalsDeclared.has(t)) {
|
|
3870
|
+
touchesGlobal = true;
|
|
3871
|
+
break;
|
|
3872
|
+
}
|
|
3873
|
+
}
|
|
3874
|
+
if (touchesGlobal) globalsOut.push({ intentId: intentId || "", oid: oid.slice(0, 10), blob });
|
|
3875
|
+
}
|
|
3876
|
+
} else {
|
|
3877
|
+
const wh = String(res.error || "").match(/wrong-home:(s\d+)/);
|
|
3878
|
+
if (wh) {
|
|
3879
|
+
const coordinator = shardRouting && shardRouting.coordinatorWs || (ws.match(/^(.*)--s\d+$/) || [])[1] || ws;
|
|
3880
|
+
rejected.push({
|
|
3881
|
+
oid: oid.slice(0, 10),
|
|
3882
|
+
error: `wrong-home: this write's home is assigned to '${coordinator}--${wh[1]}' \u2014 the chain gate's homing invariant refused it; a routed client re-routes and retries automatically`,
|
|
3883
|
+
wrongHome: { workspace: `${coordinator}--${wh[1]}` }
|
|
3884
|
+
});
|
|
3885
|
+
continue;
|
|
3886
|
+
}
|
|
3887
|
+
const error = `${String(res.error || "").slice(0, 300)} \u2014 dead-lettered; fix the law (or the payload), then POST /v1/workspaces/${ws}/dead-letters/retry`;
|
|
3888
|
+
rejected.push({ oid: oid.slice(0, 10), error, deadLettered: true });
|
|
3889
|
+
deadLetters.push({ intentId, oid: oid.slice(0, 10), session: cid, error, blob, domain: dom, directiveId: dirId });
|
|
3890
|
+
}
|
|
3891
|
+
} catch (e) {
|
|
3892
|
+
rejected.push({ oid: oid.slice(0, 10), error: String(e).slice(0, 200) });
|
|
3893
|
+
}
|
|
3894
|
+
}
|
|
3895
|
+
_gateDone({ admitted: admitted.length, rejected: rejected.length, skipped: skipped.length });
|
|
3896
|
+
_intents += admitted.length;
|
|
3897
|
+
totalAdmitted += admitted.length;
|
|
3898
|
+
consumed.push({ cid, ref });
|
|
3899
|
+
results.push({ session: cid, admitted, rejected, ...skipped.length ? { skipped } : {} });
|
|
3900
|
+
} catch (e) {
|
|
3901
|
+
results.push({ session: cid, error: String(e).slice(0, 200) });
|
|
3902
|
+
} finally {
|
|
3903
|
+
_sessionDone();
|
|
3904
|
+
}
|
|
3905
|
+
}
|
|
3906
|
+
if (canDeferProjection && totalAdmitted) {
|
|
3907
|
+
const _projDone = _span("projection flush (batch)", "cproject", 1, { admitted: totalAdmitted });
|
|
3908
|
+
try {
|
|
3909
|
+
refreshProjection(eng2, ws);
|
|
3910
|
+
} catch (e) {
|
|
3911
|
+
log("admit-projection-flush-failed", { ws, error: String(e).slice(0, 200) });
|
|
3912
|
+
} finally {
|
|
3913
|
+
_projDone();
|
|
3914
|
+
}
|
|
3915
|
+
}
|
|
3916
|
+
let unparkedSessions = /* @__PURE__ */ new Set();
|
|
3917
|
+
if (deadLetters.length) {
|
|
3918
|
+
try {
|
|
3919
|
+
unparkedSessions = (await materializeRefused(eng2, ws, ledger, deadLetters)).unparkedSessions;
|
|
3920
|
+
} catch (e) {
|
|
3921
|
+
unparkedSessions = new Set(deadLetters.map((dl) => dl.session).filter(Boolean));
|
|
3922
|
+
log("refused-materialize-failed", { ws, error: String(e).slice(0, 200) });
|
|
3923
|
+
}
|
|
3924
|
+
}
|
|
3925
|
+
const prunable = unparkedSessions.size ? consumed.filter((c) => !unparkedSessions.has(c.cid)) : consumed;
|
|
3926
|
+
const hasCoordination = !!(summaryCtx || placements.length || splits.length || globalsOut.length);
|
|
3927
|
+
const deferDurability = defer && !hasCoordination;
|
|
3928
|
+
let _sealed = true;
|
|
3929
|
+
if (totalAdmitted && !deferDurability) {
|
|
3930
|
+
const _sealDone = _span(`seal (batch)`, "cseal", 1, { sessions: consumed.length, admitted: totalAdmitted });
|
|
3931
|
+
const _s = Date.now();
|
|
3932
|
+
try {
|
|
3933
|
+
await commitAndPush(eng2, ws, ledger);
|
|
3934
|
+
} catch (e) {
|
|
3935
|
+
_sealed = false;
|
|
3936
|
+
log("admit-batch-push-failed", { ws, sessions: consumed.length, admitted: totalAdmitted, error: String(e).slice(0, 200) });
|
|
3937
|
+
} finally {
|
|
3938
|
+
_sealMs += Date.now() - _s;
|
|
3939
|
+
_sealDone();
|
|
3940
|
+
}
|
|
3941
|
+
}
|
|
3942
|
+
if (_sealed && !deferDurability) {
|
|
3943
|
+
for (const { ref } of prunable) {
|
|
3944
|
+
try {
|
|
3945
|
+
await custodyPush(eng2, ws, ledger, { remoteRef: ref, del: true });
|
|
3946
|
+
} catch {
|
|
3947
|
+
}
|
|
3948
|
+
}
|
|
3949
|
+
}
|
|
3950
|
+
const newMain = kgit.resolveRef(eng2, gitdir, BRANCH);
|
|
3951
|
+
log("admit", { ws, sessions: results.length, main: newMain });
|
|
3952
|
+
const summary = summaryCtx ? summaryOfCapture(summaryCtx, headBefore, newMain) : null;
|
|
3953
|
+
const _total = Date.now() - _t0;
|
|
3954
|
+
const timings = { totalMs: _total, scanMs: _scanMs, fetchMs: _fetchMs, sealMs: _sealMs, gateFoldMs: Math.max(0, _total - _scanMs - _fetchMs - _sealMs), intents: _intents, sessions: results.length, spans: _spans };
|
|
3955
|
+
return {
|
|
3956
|
+
sessions: results,
|
|
3957
|
+
main: newMain,
|
|
3958
|
+
deadLetters,
|
|
3959
|
+
timings,
|
|
3960
|
+
...summary ? { summary } : {},
|
|
3961
|
+
...placements.length ? { placements } : {},
|
|
3962
|
+
...splits.length ? { splits } : {},
|
|
3963
|
+
...globalsOut.length ? { globals: globalsOut } : {},
|
|
3964
|
+
// The adapter's backup lane consumes these: `deferred` ⇒ resident main carries totalAdmitted
|
|
3965
|
+
// un-backed-up commits and `consumed` are the session branches to delete AFTER the push lands.
|
|
3966
|
+
...deferDurability && totalAdmitted ? { deferred: true, consumed: prunable, admitted: totalAdmitted } : {}
|
|
3967
|
+
};
|
|
3968
|
+
}
|
|
3969
|
+
async function backupHead(eng2, ws, ledger, head) {
|
|
3970
|
+
if (!eng2.mounted.has(ws)) return { ok: false, error: "not mounted" };
|
|
3971
|
+
if (!head) return { ok: false, error: "no head" };
|
|
3972
|
+
try {
|
|
3973
|
+
await custodyPush(eng2, ws, ledger, { remoteRef: "refs/heads/main", newOid: head });
|
|
3974
|
+
return { ok: true, head };
|
|
3975
|
+
} catch (e) {
|
|
3976
|
+
return { ok: false, error: String(e?.message || e).slice(0, 200), head };
|
|
3977
|
+
}
|
|
3978
|
+
}
|
|
3979
|
+
async function backupToCustody(eng2, ws, ledger, { consumed = [] } = {}) {
|
|
3980
|
+
if (!eng2.mounted.has(ws)) return { ok: false, error: "not mounted" };
|
|
3981
|
+
const gitdir = gitdirOf(ws);
|
|
3982
|
+
let head = null;
|
|
3983
|
+
head = kgit.resolveRef(eng2, gitdir, BRANCH);
|
|
3984
|
+
if (!head) return { ok: false, error: "no local main" };
|
|
3985
|
+
try {
|
|
3986
|
+
await custodyPush(eng2, ws, ledger, { remoteRef: "refs/heads/main" });
|
|
3987
|
+
} catch (e) {
|
|
3988
|
+
return { ok: false, error: String(e).slice(0, 200), head };
|
|
3989
|
+
}
|
|
3990
|
+
for (const { ref } of consumed) {
|
|
3991
|
+
try {
|
|
3992
|
+
await custodyPush(eng2, ws, ledger, { remoteRef: ref, del: true });
|
|
3993
|
+
} catch {
|
|
3994
|
+
}
|
|
3995
|
+
}
|
|
3996
|
+
return { ok: true, head };
|
|
3997
|
+
}
|
|
3998
|
+
async function verifyChain(eng2, ws, ledger) {
|
|
3999
|
+
unmount(eng2, ws);
|
|
4000
|
+
const m = await mountWorkspace(eng2, ws, ledger);
|
|
4001
|
+
if (!m.restored) {
|
|
4002
|
+
return { valid: false, check: "mount", error: m.restoreError || "no refs/heads/main on the ledger \u2014 nothing to verify (push refs/heads/main to deliver a chain first)" };
|
|
4003
|
+
}
|
|
4004
|
+
try {
|
|
4005
|
+
return JSON.parse(call(eng2.ex, "query", { repoArg: repoArgOf(ws), workspace: ws, queryBytes: b64Json({ op: "verifyChain" }), branch: BRANCH }, eng2.STDERR));
|
|
4006
|
+
} finally {
|
|
4007
|
+
unmount(eng2, ws);
|
|
4008
|
+
}
|
|
4009
|
+
}
|
|
4010
|
+
function verifyChainLocal(eng2, ws) {
|
|
4011
|
+
return JSON.parse(call(eng2.ex, "query", { repoArg: repoArgOf(ws), workspace: ws, queryBytes: b64Json({ op: "verifyChain" }), branch: BRANCH }, eng2.STDERR));
|
|
4012
|
+
}
|
|
4013
|
+
function historyPreflight(eng2, ws, domain, candidateHash, atHash, dispositions) {
|
|
4014
|
+
return JSON.parse(call(eng2.ex, "query", {
|
|
4015
|
+
repoArg: repoArgOf(ws),
|
|
4016
|
+
workspace: ws,
|
|
4017
|
+
branch: BRANCH,
|
|
4018
|
+
queryBytes: b64Json({
|
|
4019
|
+
op: "historyPreflight",
|
|
4020
|
+
domain,
|
|
4021
|
+
candidateHash,
|
|
4022
|
+
atHash,
|
|
4023
|
+
...dispositions ? { dispositions } : {}
|
|
4024
|
+
})
|
|
4025
|
+
}, eng2.STDERR));
|
|
4026
|
+
}
|
|
4027
|
+
function historyRestorePreflight(eng2, ws, domain, currentHash, priorHash, atHash, actor) {
|
|
4028
|
+
return JSON.parse(call(eng2.ex, "query", {
|
|
4029
|
+
repoArg: repoArgOf(ws),
|
|
4030
|
+
workspace: ws,
|
|
4031
|
+
branch: BRANCH,
|
|
4032
|
+
queryBytes: b64Json({
|
|
4033
|
+
op: "historyPreflight",
|
|
4034
|
+
domain,
|
|
4035
|
+
candidateHash: priorHash,
|
|
4036
|
+
atHash,
|
|
4037
|
+
restorePreviousOf: currentHash,
|
|
4038
|
+
ackBy: actor
|
|
4039
|
+
})
|
|
4040
|
+
}, eng2.STDERR));
|
|
4041
|
+
}
|
|
4042
|
+
function promotePreflighted(eng2, ws, domain, candidateHash, opts = {}) {
|
|
4043
|
+
const actingPrincipal = opts.actor?.trim() || String(eng2.installedBy || "").trim();
|
|
4044
|
+
if (!actingPrincipal && !opts.authToken) {
|
|
4045
|
+
return {
|
|
4046
|
+
ok: false,
|
|
4047
|
+
stage: "identity",
|
|
4048
|
+
error: "promotion needs the stateful client's current principal so its business decision is attributable"
|
|
4049
|
+
};
|
|
4050
|
+
}
|
|
4051
|
+
const atHash = kgit.resolveRef(eng2, gitdirOf(ws), BRANCH);
|
|
4052
|
+
if (!atHash) {
|
|
4053
|
+
return { ok: false, stage: "preflight", error: "workspace has no history to prove" };
|
|
4054
|
+
}
|
|
4055
|
+
let report;
|
|
4056
|
+
try {
|
|
4057
|
+
report = historyPreflight(eng2, ws, domain, candidateHash, atHash, opts.dispositions);
|
|
4058
|
+
} catch (error) {
|
|
4059
|
+
return { ok: false, stage: "preflight", error: String(error?.message || error) };
|
|
4060
|
+
}
|
|
4061
|
+
const prepared = prepareManagedPromotion(report, candidateHash, domain, opts);
|
|
4062
|
+
if (!prepared.ok) return prepared;
|
|
4063
|
+
const {
|
|
4064
|
+
replaces: _replaces,
|
|
4065
|
+
retireReplaced: _retireReplaced,
|
|
4066
|
+
dispositions: _dispositions,
|
|
4067
|
+
acceptanceReason: _acceptance,
|
|
4068
|
+
...authorOpts
|
|
4069
|
+
} = opts;
|
|
4070
|
+
const result = author(eng2, ws, "nomos", "promoteDomain", prepared.payload, eng2.hashes.nomos, {
|
|
4071
|
+
...authorOpts,
|
|
4072
|
+
...actingPrincipal ? { actor: actingPrincipal } : {}
|
|
4073
|
+
});
|
|
4074
|
+
return result.ok ? { ...result, stage: "promoted", report, receiptId: prepared.receiptId } : { ...result, stage: "admission", report, receiptId: prepared.receiptId };
|
|
4075
|
+
}
|
|
4076
|
+
function restorePreviousPreflighted(eng2, ws, domain, currentHash, opts = {}) {
|
|
4077
|
+
const actingPrincipal = opts.actor?.trim() || String(eng2.installedBy || "").trim();
|
|
4078
|
+
if (!actingPrincipal) {
|
|
4079
|
+
return {
|
|
4080
|
+
ok: false,
|
|
4081
|
+
stage: "identity",
|
|
4082
|
+
error: "model restoration needs the stateful client's current principal so its business decision is attributable"
|
|
4083
|
+
};
|
|
4084
|
+
}
|
|
4085
|
+
const resolved = currentLaw(eng2, ws)[domain];
|
|
4086
|
+
if (resolved !== currentHash) {
|
|
4087
|
+
return {
|
|
4088
|
+
ok: false,
|
|
4089
|
+
stage: "preflight",
|
|
4090
|
+
error: "the generated model is no longer current; nothing changed [nomos.rollout.restore-current-changed]"
|
|
4091
|
+
};
|
|
4092
|
+
}
|
|
4093
|
+
const current = qById(eng2, ws, `domain-installation:${currentHash}`)[0]?.data;
|
|
4094
|
+
const priorHash = typeof current?.["spec.supersedes"] === "string" ? current["spec.supersedes"] : "";
|
|
4095
|
+
if (!priorHash) {
|
|
4096
|
+
return {
|
|
4097
|
+
ok: false,
|
|
4098
|
+
stage: "preflight",
|
|
4099
|
+
error: "this model has no previous model to restore [nomos.rollout.restore-no-previous]"
|
|
4100
|
+
};
|
|
4101
|
+
}
|
|
4102
|
+
const atHash = kgit.resolveRef(eng2, gitdirOf(ws), BRANCH);
|
|
4103
|
+
if (!atHash) return { ok: false, stage: "preflight", error: "workspace has no history to prove" };
|
|
4104
|
+
let report;
|
|
4105
|
+
try {
|
|
4106
|
+
report = historyRestorePreflight(
|
|
4107
|
+
eng2,
|
|
4108
|
+
ws,
|
|
4109
|
+
domain,
|
|
4110
|
+
currentHash,
|
|
4111
|
+
priorHash,
|
|
4112
|
+
atHash,
|
|
4113
|
+
actingPrincipal
|
|
4114
|
+
);
|
|
4115
|
+
} catch (error) {
|
|
4116
|
+
return { ok: false, stage: "preflight", error: String(error?.message || error) };
|
|
4117
|
+
}
|
|
4118
|
+
const prepared = prepareManagedRestoration(report, currentHash, priorHash, domain, opts);
|
|
4119
|
+
if (!prepared.ok) return prepared;
|
|
4120
|
+
const { acceptanceReason: _acceptance, ...authorOpts } = opts;
|
|
4121
|
+
const result = author(eng2, ws, "nomos", "rollbackDomain", prepared.payload, eng2.hashes.nomos, {
|
|
4122
|
+
...authorOpts,
|
|
4123
|
+
...actingPrincipal ? { actor: actingPrincipal } : {}
|
|
4124
|
+
});
|
|
4125
|
+
return result.ok ? { ...result, stage: "restored", report, receiptId: prepared.receiptId } : { ...result, stage: "admission", report, receiptId: prepared.receiptId };
|
|
4126
|
+
}
|
|
4127
|
+
async function rebirthCompact(eng2, ws, ledger, { reason = "", supersededAt = 0, parent = "" } = {}) {
|
|
4128
|
+
const incWs = `${ws}__rebirth`;
|
|
4129
|
+
try {
|
|
4130
|
+
if (!eng2.mounted.has(ws)) await mountWorkspace(eng2, ws, ledger || { remote: "https://unborn.invalid/", headers: {} });
|
|
4131
|
+
const srcGitdir = gitdirOf(ws);
|
|
4132
|
+
const oldHead = kgit.resolveRef(eng2, srcGitdir, BRANCH);
|
|
4133
|
+
if (!/^[0-9a-f]{40}$/i.test(oldHead || "")) return { ok: false, error: `'${ws}' has no refs/heads/main to rebirth \u2014 nothing to compact` };
|
|
4134
|
+
const sealed = kgit.intentsAbove(eng2, srcGitdir, oldHead, "", 0);
|
|
4135
|
+
if (!Array.isArray(sealed) || !sealed.length) return { ok: false, error: `'${ws}' main carries no intents to replay` };
|
|
4136
|
+
unmount(eng2, incWs);
|
|
4137
|
+
await mountEphemeralWorkspace(eng2, incWs, {});
|
|
4138
|
+
const isGenesisBootstrapInstall = (c) => {
|
|
4139
|
+
try {
|
|
4140
|
+
const doc = JSON.parse(dec3.decode(bytesFromB64(c.intentB64)));
|
|
4141
|
+
return doc?.payload?.domain === "bootstrap" && doc?.payload?.directiveId === "installDomain";
|
|
4142
|
+
} catch {
|
|
4143
|
+
return false;
|
|
4144
|
+
}
|
|
4145
|
+
};
|
|
4146
|
+
const transformState = (sealedBytes, _ctx) => sealedBytes;
|
|
4147
|
+
const carried = [];
|
|
4148
|
+
const parkedIntents = [];
|
|
4149
|
+
const parkedBlobs = [];
|
|
4150
|
+
let idx = -1, lastCarriedIdx = -1, firstParkedIdx = -1;
|
|
4151
|
+
for (const c of sealed) {
|
|
4152
|
+
idx++;
|
|
4153
|
+
if (isGenesisBootstrapInstall(c)) continue;
|
|
4154
|
+
let bytes;
|
|
4155
|
+
try {
|
|
4156
|
+
bytes = transformState(bytesFromB64(c.intentB64), { oid: c.oid, incWs });
|
|
4157
|
+
} catch {
|
|
4158
|
+
parkedIntents.push({ oid: c.oid, reason: "undecodable intent blob" });
|
|
4159
|
+
parkedBlobs.push(bytesFromB64(c.intentB64));
|
|
4160
|
+
if (firstParkedIdx < 0) firstParkedIdx = idx;
|
|
4161
|
+
continue;
|
|
4162
|
+
}
|
|
4163
|
+
const r = applyIntentBytes(eng2, incWs, bytes, { defer: false });
|
|
4164
|
+
if (r && r.ok) {
|
|
4165
|
+
carried.push(c.oid);
|
|
4166
|
+
lastCarriedIdx = idx;
|
|
4167
|
+
} else {
|
|
4168
|
+
parkedIntents.push({ oid: c.oid, reason: String(r && r.error || "refused").slice(0, 200) });
|
|
4169
|
+
parkedBlobs.push(bytes);
|
|
4170
|
+
if (firstParkedIdx < 0) firstParkedIdx = idx;
|
|
4171
|
+
}
|
|
4172
|
+
}
|
|
4173
|
+
const scarShape = parkedIntents.length === 0 ? "none" : firstParkedIdx > lastCarriedIdx ? "tail" : "mid-chain";
|
|
4174
|
+
const incGitdir = gitdirOf(incWs);
|
|
4175
|
+
const head = kgit.resolveRef(eng2, incGitdir, BRANCH);
|
|
4176
|
+
if (!/^[0-9a-f]{40}$/i.test(head || "")) {
|
|
4177
|
+
unmount(eng2, incWs);
|
|
4178
|
+
return { ok: false, error: `the incarnation folded no intents (all ${sealed.length} refused \u2014 all parked-to-be, nothing to serve)`, parked: parkedIntents.length, parkedIntents };
|
|
4179
|
+
}
|
|
4180
|
+
const incChain = kgit.intentsAbove(eng2, incGitdir, head, "", 0);
|
|
4181
|
+
const genesisOid = incChain.length ? incChain[0].oid : head;
|
|
4182
|
+
let parkedRef = "";
|
|
4183
|
+
let parkedTip = "";
|
|
4184
|
+
if (parkedBlobs.length) {
|
|
4185
|
+
parkedRef = `refs/heads/unreplayed/${supersededAt || Date.now()}`;
|
|
4186
|
+
let parentOid = head;
|
|
4187
|
+
for (let i = 0; i < parkedBlobs.length; i++) {
|
|
4188
|
+
parentOid = kgit.writeMetaCommit(eng2, incGitdir, {
|
|
4189
|
+
parents: [parentOid],
|
|
4190
|
+
files: [{ path: "intent.json", bytes: parkedBlobs[i] }],
|
|
4191
|
+
message: `unreplayed: ${parkedIntents[i].oid.slice(0, 12)} \u2014 ${parkedIntents[i].reason.slice(0, 120)}`,
|
|
4192
|
+
timeSecs: 0,
|
|
4193
|
+
ref: i === parkedBlobs.length - 1 ? parkedRef : ""
|
|
4194
|
+
});
|
|
4195
|
+
}
|
|
4196
|
+
parkedTip = parentOid;
|
|
4197
|
+
}
|
|
4198
|
+
const verdict = verifyChainLocal(eng2, incWs);
|
|
4199
|
+
const verified = verdict && verdict.valid === true;
|
|
4200
|
+
let packB64 = "";
|
|
4201
|
+
let parkedPackB64 = "";
|
|
4202
|
+
if (verified) {
|
|
4203
|
+
const pack = packDelta(eng2, incWs, "", head);
|
|
4204
|
+
if (pack && pack.length) packB64 = b64Bytes(pack);
|
|
4205
|
+
if (parkedTip) {
|
|
4206
|
+
const ppack = packDelta(eng2, incWs, head, parkedTip);
|
|
4207
|
+
if (ppack && ppack.length) parkedPackB64 = b64Bytes(ppack);
|
|
4208
|
+
if (!parkedPackB64) {
|
|
4209
|
+
unmount(eng2, incWs);
|
|
4210
|
+
return { ok: false, error: "parked intents exist but produced no parked pack \u2014 refusing (parked work must never be lost)", parked: parkedIntents.length };
|
|
4211
|
+
}
|
|
4212
|
+
}
|
|
4213
|
+
}
|
|
4214
|
+
unmount(eng2, incWs);
|
|
4215
|
+
if (verified && !packB64) return { ok: false, error: "the incarnation self-verified but produced no pack", verified: true };
|
|
4216
|
+
return {
|
|
4217
|
+
ok: true,
|
|
4218
|
+
packB64,
|
|
4219
|
+
head,
|
|
4220
|
+
genesisOid,
|
|
4221
|
+
verified,
|
|
4222
|
+
...verified ? {} : { verifyError: verdict && verdict.error || "verify_chain returned invalid" },
|
|
4223
|
+
carried: carried.length,
|
|
4224
|
+
parked: parkedIntents.length,
|
|
4225
|
+
parkedIntents,
|
|
4226
|
+
scarShape,
|
|
4227
|
+
...parkedTip ? { parkedRef, parkedTip, parkedPackB64 } : {},
|
|
4228
|
+
// rebornFrom = the SUPERSEDED chain's genesis oid (the old ancestry the reborn holon descends
|
|
4229
|
+
// from). Surfaced for the worker's status contract; NOT re-authored IN-CHAIN here (stamping it
|
|
4230
|
+
// onto birthCloud would re-sign the record → the operator key, the CLI lane).
|
|
4231
|
+
rebornFrom: sealed[0].oid,
|
|
4232
|
+
parent,
|
|
4233
|
+
reason,
|
|
4234
|
+
supersededAt
|
|
4235
|
+
};
|
|
4236
|
+
} catch (e) {
|
|
4237
|
+
try {
|
|
4238
|
+
unmount(eng2, incWs);
|
|
4239
|
+
} catch {
|
|
4240
|
+
}
|
|
4241
|
+
return { ok: false, error: String(e?.message || e).slice(0, 300) };
|
|
4242
|
+
}
|
|
4243
|
+
}
|
|
4244
|
+
async function archiveRefusedMain(eng2, ws, ledger, label) {
|
|
4245
|
+
unmount(eng2, ws);
|
|
4246
|
+
const m = await mountWorkspace(eng2, ws, ledger);
|
|
4247
|
+
if (!m.restored) {
|
|
4248
|
+
unmount(eng2, ws);
|
|
4249
|
+
return { archived: false, error: "no main to archive" };
|
|
4250
|
+
}
|
|
4251
|
+
const gitdir = gitdirOf(ws);
|
|
4252
|
+
try {
|
|
4253
|
+
await custodyPush(eng2, ws, ledger, { localRef: "refs/heads/main", remoteRef: `refs/heads/refused/${label}`, pointer: true });
|
|
4254
|
+
await custodyPush(eng2, ws, ledger, { remoteRef: "refs/heads/main", del: true });
|
|
4255
|
+
return { archived: true, ref: `refs/heads/refused/${label}` };
|
|
4256
|
+
} finally {
|
|
4257
|
+
unmount(eng2, ws);
|
|
4258
|
+
}
|
|
4259
|
+
}
|
|
4260
|
+
var refusedRefName = (offerId) => `refs/heads/refused/${String(offerId || "unknown").replace(/[^A-Za-z0-9._-]/g, "_")}`;
|
|
4261
|
+
async function writeRefusedRef(eng2, ws, dl) {
|
|
4262
|
+
const gitdir = gitdirOf(ws);
|
|
4263
|
+
const blob = dl.blob instanceof Uint8Array ? dl.blob : enc3.encode(String(dl.blob || ""));
|
|
4264
|
+
const refForStamp = refusedRefName(dl.intentId || dl.oid);
|
|
4265
|
+
let parkedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
4266
|
+
try {
|
|
4267
|
+
const prevOid = kgit.resolveRef(eng2, gitdir, refForStamp);
|
|
4268
|
+
if (prevOid) {
|
|
4269
|
+
const prev = JSON.parse(dec3.decode(kgit.readBlob(eng2, gitdir, prevOid, "verdict.json") ?? new Uint8Array()));
|
|
4270
|
+
if (prev && typeof prev.parkedAt === "string") parkedAt = prev.parkedAt;
|
|
4271
|
+
}
|
|
4272
|
+
} catch {
|
|
4273
|
+
}
|
|
4274
|
+
const verdict = enc3.encode(JSON.stringify({
|
|
4275
|
+
offerId: dl.intentId || null,
|
|
4276
|
+
oid: dl.oid || null,
|
|
4277
|
+
domain: dl.domain || null,
|
|
4278
|
+
directiveId: dl.directiveId || null,
|
|
4279
|
+
session: dl.session || null,
|
|
4280
|
+
parkedAt,
|
|
4281
|
+
verdict: { reason: String(dl.error || "refused") }
|
|
4282
|
+
}));
|
|
4283
|
+
const ref = refusedRefName(dl.intentId || dl.oid);
|
|
4284
|
+
kgit.writeMetaCommit(eng2, gitdir, {
|
|
4285
|
+
parents: [],
|
|
4286
|
+
files: [{ path: "intent.json", bytes: blob }, { path: "verdict.json", bytes: verdict }],
|
|
4287
|
+
message: `refused: ${dl.domain || "?"}/${dl.directiveId || "?"} \u2014 ${String(dl.error || "").slice(0, 120)}`,
|
|
4288
|
+
timeSecs: 0,
|
|
4289
|
+
ref
|
|
4290
|
+
});
|
|
4291
|
+
return ref;
|
|
4292
|
+
}
|
|
4293
|
+
async function listRefusedRefs(eng2, ws) {
|
|
4294
|
+
const gitdir = gitdirOf(ws);
|
|
4295
|
+
const refs = kgit.listRefs(eng2, gitdir, "refs/heads/refused/");
|
|
4296
|
+
const out = [];
|
|
4297
|
+
for (const { ref, oid } of refs) {
|
|
4298
|
+
try {
|
|
4299
|
+
const vb = kgit.readBlob(eng2, gitdir, oid, "verdict.json") ?? new Uint8Array();
|
|
4300
|
+
const ib = kgit.readBlob(eng2, gitdir, oid, "intent.json") ?? new Uint8Array();
|
|
4301
|
+
const v = JSON.parse(dec3.decode(vb));
|
|
4302
|
+
out.push({ ref, ...v, intentB64: b64Bytes(ib) });
|
|
4303
|
+
} catch {
|
|
4304
|
+
}
|
|
4305
|
+
}
|
|
4306
|
+
return out;
|
|
4307
|
+
}
|
|
4308
|
+
async function refusedSummary(eng2, ws) {
|
|
4309
|
+
const gitdir = gitdirOf(ws);
|
|
4310
|
+
const refs = kgit.listRefs(eng2, gitdir, "refs/heads/refused/");
|
|
4311
|
+
let count2 = 0, latestParkedAt = null;
|
|
4312
|
+
for (const { oid } of refs) {
|
|
4313
|
+
try {
|
|
4314
|
+
const v = JSON.parse(dec3.decode(kgit.readBlob(eng2, gitdir, oid, "verdict.json") ?? new Uint8Array()));
|
|
4315
|
+
count2++;
|
|
4316
|
+
if (v && typeof v.parkedAt === "string" && (!latestParkedAt || v.parkedAt > latestParkedAt)) latestParkedAt = v.parkedAt;
|
|
4317
|
+
} catch {
|
|
4318
|
+
}
|
|
4319
|
+
}
|
|
4320
|
+
return { count: count2, latestParkedAt };
|
|
4321
|
+
}
|
|
4322
|
+
async function materializeRefused(eng2, ws, ledger, deadLetters) {
|
|
4323
|
+
const refs = [];
|
|
4324
|
+
const unparkedSessions = /* @__PURE__ */ new Set();
|
|
4325
|
+
for (const dl of deadLetters || []) {
|
|
4326
|
+
try {
|
|
4327
|
+
const ref = await writeRefusedRef(eng2, ws, dl);
|
|
4328
|
+
await custodyPush(eng2, ws, ledger, { remoteRef: ref });
|
|
4329
|
+
refs.push(ref);
|
|
4330
|
+
} catch (e) {
|
|
4331
|
+
if (dl && dl.session) unparkedSessions.add(dl.session);
|
|
4332
|
+
log("refused-ref-materialize-failed", { ws, intentId: dl?.intentId, error: String(e).slice(0, 200) });
|
|
4333
|
+
}
|
|
4334
|
+
}
|
|
4335
|
+
return { refs, unparkedSessions };
|
|
4336
|
+
}
|
|
4337
|
+
async function deleteRefusedRef(eng2, ws, ledger, offerId) {
|
|
4338
|
+
const gitdir = gitdirOf(ws);
|
|
4339
|
+
const ref = refusedRefName(offerId);
|
|
4340
|
+
try {
|
|
4341
|
+
kgit.deleteRef(eng2, gitdir, ref);
|
|
4342
|
+
} catch {
|
|
4343
|
+
}
|
|
4344
|
+
try {
|
|
4345
|
+
await custodyPush(eng2, ws, ledger, { remoteRef: ref, del: true });
|
|
4346
|
+
} catch (e) {
|
|
4347
|
+
log("refused-ref-delete-push-failed", { ws, ref, error: String(e).slice(0, 200) });
|
|
4348
|
+
}
|
|
4349
|
+
return ref;
|
|
4350
|
+
}
|
|
4351
|
+
var outboxRefName = (requestId) => `refs/heads/outbox/${String(requestId || "unknown").replace(/[^A-Za-z0-9._-]/g, "_")}`;
|
|
4352
|
+
async function outboxRefExists(eng2, ws, requestId) {
|
|
4353
|
+
return !!kgit.resolveRef(eng2, gitdirOf(ws), outboxRefName(requestId));
|
|
4354
|
+
}
|
|
4355
|
+
async function writeOutboxRef(eng2, ws, rec) {
|
|
4356
|
+
const gitdir = gitdirOf(ws);
|
|
4357
|
+
const orderBlob = enc3.encode(JSON.stringify(rec.order ?? {}));
|
|
4358
|
+
const delivery = enc3.encode(JSON.stringify({
|
|
4359
|
+
requestId: rec.requestId || null,
|
|
4360
|
+
capability: rec.capability || null,
|
|
4361
|
+
url: rec.url || null,
|
|
4362
|
+
status: rec.status || "delivered",
|
|
4363
|
+
httpStatus: rec.httpStatus ?? null
|
|
4364
|
+
}));
|
|
4365
|
+
const ref = outboxRefName(rec.requestId);
|
|
4366
|
+
kgit.writeMetaCommit(eng2, gitdir, {
|
|
4367
|
+
parents: [],
|
|
4368
|
+
files: [{ path: "order.json", bytes: orderBlob }, { path: "delivery.json", bytes: delivery }],
|
|
4369
|
+
message: `outbox: ${rec.capability || "?"} \u2192 ${String(rec.url || "?").slice(0, 80)}`,
|
|
4370
|
+
timeSecs: 0,
|
|
4371
|
+
ref
|
|
4372
|
+
});
|
|
4373
|
+
return ref;
|
|
4374
|
+
}
|
|
4375
|
+
async function emitOutbox(eng2, ws, ledger) {
|
|
4376
|
+
const orders = (() => {
|
|
4377
|
+
try {
|
|
4378
|
+
return reactorScan(eng2, ws);
|
|
4379
|
+
} catch {
|
|
4380
|
+
return [];
|
|
4381
|
+
}
|
|
4382
|
+
})();
|
|
4383
|
+
if (!Array.isArray(orders) || orders.length === 0) return { delivered: [], skipped: 0, errors: 0 };
|
|
4384
|
+
const byType = /* @__PURE__ */ new Map();
|
|
4385
|
+
for (const c of declaredCapabilities(eng2, ws)) if (c && c.aggregateId) byType.set(c.aggregateId, c);
|
|
4386
|
+
const gitdir = gitdirOf(ws);
|
|
4387
|
+
const delivered = [];
|
|
4388
|
+
let skipped = 0, errors = 0;
|
|
4389
|
+
for (const order of orders) {
|
|
4390
|
+
const cap = byType.get(order.taskType);
|
|
4391
|
+
if (!cap || !cap.endpoint) {
|
|
4392
|
+
skipped++;
|
|
4393
|
+
continue;
|
|
4394
|
+
}
|
|
4395
|
+
if (await outboxRefExists(eng2, ws, order.requestId)) {
|
|
4396
|
+
skipped++;
|
|
4397
|
+
continue;
|
|
4398
|
+
}
|
|
4399
|
+
try {
|
|
4400
|
+
const res = await fetch(cap.endpoint, {
|
|
4401
|
+
method: "POST",
|
|
4402
|
+
headers: { "content-type": "application/json", "x-nomos-capability": cap.capability, "x-nomos-workspace": ws },
|
|
4403
|
+
body: JSON.stringify({ capability: cap.capability, requestId: order.requestId, taskType: order.taskType, params: order.params, scheduledAt: order.scheduledAt, deadlineAt: order.deadlineAt }),
|
|
4404
|
+
signal: AbortSignal.timeout(3e4)
|
|
4405
|
+
});
|
|
4406
|
+
if (!res.ok) {
|
|
4407
|
+
errors++;
|
|
4408
|
+
log("outbox-deliver-non2xx", { ws, requestId: order.requestId, url: cap.endpoint, status: res.status });
|
|
4409
|
+
continue;
|
|
4410
|
+
}
|
|
4411
|
+
const ref = await writeOutboxRef(eng2, ws, { requestId: order.requestId, capability: cap.capability, url: cap.endpoint, order, status: "delivered", httpStatus: res.status });
|
|
4412
|
+
try {
|
|
4413
|
+
await custodyPush(eng2, ws, ledger, { remoteRef: ref });
|
|
4414
|
+
} catch (e) {
|
|
4415
|
+
log("outbox-ref-push-failed", { ws, ref, error: String(e).slice(0, 200) });
|
|
4416
|
+
}
|
|
4417
|
+
delivered.push(order.requestId);
|
|
4418
|
+
} catch (e) {
|
|
4419
|
+
errors++;
|
|
4420
|
+
log("outbox-deliver-failed", { ws, requestId: order.requestId, url: cap.endpoint, error: String(e).slice(0, 200) });
|
|
4421
|
+
}
|
|
4422
|
+
}
|
|
4423
|
+
if (delivered.length || errors) log("outbox", { ws, delivered: delivered.length, skipped, errors });
|
|
4424
|
+
return { delivered, skipped, errors };
|
|
4425
|
+
}
|
|
4426
|
+
var GENESIS_WARRANT_ID = "genesis-warrant:self";
|
|
4427
|
+
var isWarranted = (eng2, ws) => qById(eng2, ws, GENESIS_WARRANT_ID).length > 0;
|
|
4428
|
+
async function boot(eng2, ws, ledger) {
|
|
4429
|
+
const m = await mountWorkspace(eng2, ws, ledger);
|
|
4430
|
+
if (nomosActive(eng2, ws)) {
|
|
4431
|
+
if (ws !== "root" && !isWarranted(eng2, ws)) {
|
|
4432
|
+
throw new Error(
|
|
4433
|
+
`unwarranted-workspace: '${ws}' is booted but its genesis never landed a GenesisWarrant \u2014 an era-0 workspace is born ONLY by its parent's warranted+attested birth. This is a host-authored/legacy orphan and will not serve (author its birth on its parent to bring it into the holarchy lawfully).`
|
|
4434
|
+
);
|
|
4435
|
+
}
|
|
4436
|
+
return { status: "already-initialized", head: m.remoteMain ?? void 0, controllerHash: eng2.hashes.nomos, rows: qById(eng2, ws, `domain-installation:${eng2.hashes.nomos}`), mount: m };
|
|
4437
|
+
}
|
|
4438
|
+
throw new Error(
|
|
4439
|
+
`unborn-workspace: '${ws}' has no genesis and the host does not create workspaces \u2014 author its birth on its PARENT (birthChild/birthHome/an estate .births()); the parent's offer-effect materializes it in custody, then it serves from here. (root self-adopts via _ensureRootGenesis; this is not that path.)`
|
|
4440
|
+
);
|
|
4441
|
+
}
|
|
4442
|
+
async function deploy(eng2, ws, ledger, usda, domainHash, dispositions, opts = {}) {
|
|
4443
|
+
const existing = qById(eng2, ws, `domain-installation:${domainHash}`);
|
|
4444
|
+
if (existing.length > 0) {
|
|
4445
|
+
const phase = String(existing[0]?.data?.["status.phase"] ?? "");
|
|
4446
|
+
return {
|
|
4447
|
+
head: kgit.resolveRef(eng2, gitdirOf(ws), BRANCH),
|
|
4448
|
+
installation: existing,
|
|
4449
|
+
staged: phase === "Pending",
|
|
4450
|
+
alreadyInstalled: true
|
|
4451
|
+
};
|
|
4452
|
+
}
|
|
4453
|
+
const before = currentLaw(eng2, ws);
|
|
4454
|
+
const tenantHashes = [...new Set(Object.values(before).filter(
|
|
4455
|
+
(hash) => typeof hash === "string" && hash !== eng2.hashes.bootstrap && hash !== eng2.hashes.nomos
|
|
4456
|
+
))];
|
|
4457
|
+
const upgrade = tenantHashes.length > 0;
|
|
4458
|
+
let supersedes = opts.supersedes?.trim() || void 0;
|
|
4459
|
+
if (supersedes !== void 0 && !tenantHashes.includes(supersedes)) {
|
|
4460
|
+
throw new Error(
|
|
4461
|
+
`deploy-predecessor-changed: the client named ${supersedes.slice(0, 12)}\u2026 as the current model, but this workspace no longer serves it. Refresh and deploy again; nothing was committed.`
|
|
4462
|
+
);
|
|
4463
|
+
}
|
|
4464
|
+
if (upgrade && supersedes === void 0) {
|
|
4465
|
+
if (tenantHashes.length === 1) supersedes = tenantHashes[0];
|
|
4466
|
+
else {
|
|
4467
|
+
throw new Error(
|
|
4468
|
+
`deploy-predecessor-ambiguous: this workspace currently serves ${tenantHashes.length} business-model packages. Nomos will not guess which one this candidate follows; use the generated deploy client, which binds the candidate to the current model before staging. Nothing was committed.`
|
|
4469
|
+
);
|
|
4470
|
+
}
|
|
4471
|
+
}
|
|
4472
|
+
if (upgrade && dispositions && Object.keys(dispositions).length > 0) {
|
|
4473
|
+
throw new Error(
|
|
4474
|
+
"deploy-manual-evolution-refused: successor deployment no longer accepts caller-chosen schema dispositions. Schema evolution belongs to the candidate model's adoption proof; nothing was committed."
|
|
4475
|
+
);
|
|
4476
|
+
}
|
|
4477
|
+
const directiveId = upgrade ? "stageDomain" : "installDomain";
|
|
4478
|
+
const payload = upgrade ? stagePayload(domainHash, usda, eng2.installedBy, supersedes) : installPayload(domainHash, usda, eng2.installedBy, dispositions);
|
|
4479
|
+
const result = author(eng2, ws, "nomos", directiveId, payload, eng2.hashes.nomos, {
|
|
4480
|
+
actor: opts.actor ?? "",
|
|
4481
|
+
...opts.authToken ? { authToken: opts.authToken } : {},
|
|
4482
|
+
...opts.authorSecret ? { authorSecret: opts.authorSecret } : {}
|
|
4483
|
+
});
|
|
4484
|
+
if (result.ok === false) {
|
|
4485
|
+
unmount(eng2, ws);
|
|
4486
|
+
throw new Error(
|
|
4487
|
+
`deploy-refused: ${upgrade ? "staging" : "installing"} ${domainHash.slice(0, 12)}\u2026 was REFUSED by the one gate (custody unchanged): ${result.error ?? "unknown refusal"}`
|
|
4488
|
+
);
|
|
4489
|
+
}
|
|
4490
|
+
try {
|
|
4491
|
+
const installation = qById(eng2, ws, `domain-installation:${domainHash}`);
|
|
4492
|
+
if (upgrade) {
|
|
4493
|
+
const phase = String(installation[0]?.data?.["status.phase"] ?? "");
|
|
4494
|
+
if (phase !== "Pending") {
|
|
4495
|
+
throw new Error(
|
|
4496
|
+
`deploy-did-not-stage: candidate ${domainHash.slice(0, 12)}\u2026 admitted but its lifecycle phase is '${phase || "missing"}', not Pending. Custody was not pushed.`
|
|
4497
|
+
);
|
|
4498
|
+
}
|
|
4499
|
+
const after = currentLaw(eng2, ws);
|
|
4500
|
+
const moved = Object.entries(before).filter(([key, hash]) => after[key] !== hash);
|
|
4501
|
+
if (moved.length > 0 || Object.values(after).includes(domainHash)) {
|
|
4502
|
+
throw new Error(
|
|
4503
|
+
`deploy-activated-candidate: staging ${domainHash.slice(0, 12)}\u2026 changed served law (${moved.map(([key]) => key).join(", ") || "candidate became current"}). Nomos refuses to persist an upgrade that bypassed model.adopt().`
|
|
4504
|
+
);
|
|
4505
|
+
}
|
|
4506
|
+
await commitAndPush(eng2, ws, ledger);
|
|
4507
|
+
return { head: result.head, installation, staged: true, supersedes };
|
|
4508
|
+
}
|
|
4509
|
+
const law = installedLaw(eng2, ws);
|
|
4510
|
+
const installed = (law?.domains ?? []).find((row) => row?.domainHash === domainHash);
|
|
4511
|
+
const keys = installed?.domainKeys ?? installation[0]?.data?.domainKeys ?? installation[0]?.domainKeys ?? [];
|
|
4512
|
+
if (keys.length > 0) {
|
|
4513
|
+
const currentLaw3 = law?.currentLaw ?? {};
|
|
4514
|
+
const stale = keys.filter((k) => currentLaw3[k] !== domainHash);
|
|
4515
|
+
if (stale.length > 0) {
|
|
4516
|
+
throw new Error(
|
|
4517
|
+
`deploy-did-not-advance: the install of ${domainHash.slice(0, 12)}\u2026 ADMITTED but the workspace's current law still serves ${stale.map((k) => `'${k}' from ${String(currentLaw3[k]).slice(0, 12)}\u2026`).join(", ")} \u2014 refusing (custody unchanged) instead of a silent no-op. This usually means the existing installation's era/lineage prevents supersession: run \`githolon deploy --dry-run=evolve\` for the gate's verdict, supply #58 dispositions for a true removal/retype, or re-forge the workspace onto the current lineage.`
|
|
4518
|
+
);
|
|
4519
|
+
}
|
|
4520
|
+
}
|
|
4521
|
+
await commitAndPush(eng2, ws, ledger);
|
|
4522
|
+
return { head: result.head, installation, staged: false };
|
|
4523
|
+
} catch (error) {
|
|
4524
|
+
unmount(eng2, ws);
|
|
4525
|
+
throw error;
|
|
4526
|
+
}
|
|
4527
|
+
}
|
|
4528
|
+
function snapshot(eng2, ws) {
|
|
4529
|
+
const wsDir = dirOf(eng2.preopen.dir.contents.get("ws")).contents.get(ws);
|
|
4530
|
+
return serializeTree(wsDir);
|
|
4531
|
+
}
|
|
4532
|
+
function stats(eng2) {
|
|
4533
|
+
return {
|
|
4534
|
+
mounted: eng2.mounted.size,
|
|
4535
|
+
wasmLinearMemMB: +(eng2.ex.memory.buffer.byteLength / 1048576).toFixed(1)
|
|
4536
|
+
};
|
|
4537
|
+
}
|
|
4538
|
+
async function ledgerStats(eng2, ws, ledger = null) {
|
|
4539
|
+
const gitdir = gitdirOf(ws);
|
|
4540
|
+
let head = kgit.resolveRef(eng2, gitdir, BRANCH);
|
|
4541
|
+
if (!head && ledger?.remote) {
|
|
4542
|
+
try {
|
|
4543
|
+
await custodySkeleton(eng2, gitdir);
|
|
4544
|
+
const remoteMain = await custodyFetch(eng2, ws, ledger, { remoteRef: "refs/heads/main" });
|
|
4545
|
+
if (!remoteMain) return { head: null, commits: 0 };
|
|
4546
|
+
kgit.writeRef(eng2, gitdir, "HEAD", `ref: refs/heads/${BRANCH}`);
|
|
4547
|
+
head = kgit.resolveRef(eng2, gitdir, BRANCH);
|
|
4548
|
+
const mounted2 = eng2.mounted.get(ws);
|
|
4549
|
+
if (mounted2) {
|
|
4550
|
+
mounted2.restored = true;
|
|
4551
|
+
mounted2.remoteMain = remoteMain;
|
|
4552
|
+
mounted2.restoreError = null;
|
|
4553
|
+
}
|
|
4554
|
+
} catch {
|
|
4555
|
+
}
|
|
4556
|
+
}
|
|
4557
|
+
if (!head) return { head: null, commits: 0 };
|
|
4558
|
+
const mounted = eng2.mounted.get(ws);
|
|
4559
|
+
if (mounted && mounted.ledgerStats?.head === head) return mounted.ledgerStats;
|
|
4560
|
+
const commits = kgit.intentsAbove(eng2, gitdir, head).length;
|
|
4561
|
+
const out = { head, commits };
|
|
4562
|
+
if (mounted) mounted.ledgerStats = out;
|
|
4563
|
+
return out;
|
|
4564
|
+
}
|
|
4565
|
+
async function countRemoteCommits(eng2, ledger) {
|
|
4566
|
+
const head = (await custodyLsRefs(ledger, "refs/heads/main"))["refs/heads/main"] || null;
|
|
4567
|
+
if (!head) return { head: null, commits: 0 };
|
|
4568
|
+
const scratch = "__ledgercount__";
|
|
4569
|
+
try {
|
|
4570
|
+
stageWorkspaceDir(eng2, scratch);
|
|
4571
|
+
await custodySkeleton(eng2, gitdirOf(scratch));
|
|
4572
|
+
await custodyFetch(eng2, scratch, ledger, { remoteRef: "refs/heads/main" });
|
|
4573
|
+
const commits = kgit.intentsAbove(eng2, gitdirOf(scratch), head).length;
|
|
4574
|
+
return { head, commits };
|
|
4575
|
+
} finally {
|
|
4576
|
+
try {
|
|
4577
|
+
dirOf(eng2.preopen.dir.contents.get("ws")).contents.delete(scratch);
|
|
4578
|
+
} catch {
|
|
4579
|
+
}
|
|
4580
|
+
}
|
|
4581
|
+
}
|
|
4582
|
+
var projectionStats = (eng2, ws) => JSON.parse(call(eng2.ex, "query", ws ? { repoArg: repoArgOf(ws), workspace: ws, queryBytes: b64Json({ op: "projectionStats" }), branch: BRANCH } : { queryBytes: b64Json({ op: "projectionStats" }) }, eng2.STDERR));
|
|
4583
|
+
function warmEngine(eng2) {
|
|
4584
|
+
flushDeferredProjections(eng2);
|
|
4585
|
+
return JSON.parse(call(eng2.ex, "query", { repoArg: "/work", workspace: "root", queryBytes: b64Json({ op: "warm" }), branch: BRANCH }, eng2.STDERR));
|
|
4586
|
+
}
|
|
4587
|
+
function sealAttestationBatches(eng2, ws, domain, controllerHash, opts = {}) {
|
|
4588
|
+
const snap = JSON.parse(call(eng2.ex, "query", {
|
|
4589
|
+
repoArg: repoArgOf(ws),
|
|
4590
|
+
workspace: ws,
|
|
4591
|
+
branch: BRANCH,
|
|
4592
|
+
queryBytes: b64Json({ op: "attestationOpenBatch", repoArg: repoArgOf(ws), nowMs: opts.nowMs ?? Date.now(), unmounting: !!opts.unmounting })
|
|
4593
|
+
}, eng2.STDERR));
|
|
4594
|
+
const outcomes = [];
|
|
4595
|
+
for (const b of snap.batches ?? []) {
|
|
4596
|
+
if (b.leafCount === 0) continue;
|
|
4597
|
+
if (!(b.shouldSeal || opts.force)) continue;
|
|
4598
|
+
const payload = {
|
|
4599
|
+
batchId: b.batchId,
|
|
4600
|
+
batchRoot: b.batchRoot,
|
|
4601
|
+
leafCount: b.leafCount,
|
|
4602
|
+
leaves: b.leaves,
|
|
4603
|
+
firstAt: b.firstAt,
|
|
4604
|
+
lastAt: b.lastAt,
|
|
4605
|
+
headAtSeal: b.headAtSeal,
|
|
4606
|
+
foldRootAtSeal: b.foldRootAtSeal,
|
|
4607
|
+
signerKeyHash: b.signerKeyHash
|
|
4608
|
+
};
|
|
4609
|
+
const r = author(eng2, ws, domain, "recordAttestationBatch", payload, controllerHash, {
|
|
4610
|
+
actor: "workspace:read-attestor",
|
|
4611
|
+
...opts.authorOpts ?? {}
|
|
4612
|
+
});
|
|
4613
|
+
if (!r.ok) {
|
|
4614
|
+
outcomes.push({ batchId: b.batchId, ok: false, error: r.error });
|
|
4615
|
+
continue;
|
|
4616
|
+
}
|
|
4617
|
+
JSON.parse(call(eng2.ex, "query", {
|
|
4618
|
+
repoArg: repoArgOf(ws),
|
|
4619
|
+
workspace: ws,
|
|
4620
|
+
branch: BRANCH,
|
|
4621
|
+
queryBytes: b64Json({ op: "attestationBatchClear", batchId: b.batchId, leafCount: b.leafCount })
|
|
4622
|
+
}, eng2.STDERR));
|
|
4623
|
+
outcomes.push({ batchId: b.batchId, ok: true, leafCount: b.leafCount, intent: r.intentOut });
|
|
4624
|
+
}
|
|
4625
|
+
return outcomes;
|
|
4626
|
+
}
|
|
4627
|
+
|
|
4628
|
+
// runner/offline-install.ts
|
|
4629
|
+
var HERE = dirname(fileURLToPath(import.meta.url));
|
|
4630
|
+
var engineModule = process.env["NOMOS_ADAPTER_ENGINE"] ? await import(process.env["NOMOS_ADAPTER_ENGINE"]) : engine_exports;
|
|
4631
|
+
var treeModule = process.env["NOMOS_ADAPTER_TREE"] ? await import(process.env["NOMOS_ADAPTER_TREE"]) : { serializeTree };
|
|
4632
|
+
var { createEngine: createEngine2, mountWorkspace: mountWorkspace2, author: author2, installPayload: installPayload2, authorKeygen: authorKeygen2, refreshProjection: refreshProjection2, currentLaw: currentLaw2, exportCheckpoint: exportCheckpoint2 } = engineModule;
|
|
4633
|
+
var { serializeTree: serializeTree2 } = treeModule;
|
|
4634
|
+
var die = (msg) => {
|
|
4635
|
+
process.stderr.write(`offline-install: ${msg}
|
|
4636
|
+
`);
|
|
4637
|
+
process.exit(1);
|
|
4638
|
+
};
|
|
4639
|
+
var sha256hex2 = async (s) => {
|
|
4640
|
+
const b = typeof s === "string" ? new TextEncoder().encode(s) : s;
|
|
4641
|
+
const h = await wc.subtle.digest("SHA-256", b);
|
|
4642
|
+
return [...new Uint8Array(h)].map((x) => x.toString(16).padStart(2, "0")).join("");
|
|
4643
|
+
};
|
|
4644
|
+
var arg = (name) => {
|
|
4645
|
+
const i = process.argv.indexOf(name);
|
|
4646
|
+
return i >= 0 ? process.argv[i + 1] : void 0;
|
|
4647
|
+
};
|
|
4648
|
+
var deployPaths = (arg("--deploy") || "").split(",").map((s) => s.trim()).filter(Boolean);
|
|
4649
|
+
var workspace = arg("--workspace") || "test-ws";
|
|
4650
|
+
var outPath = arg("--out");
|
|
4651
|
+
var runtimeDir = arg("--runtime") || process.env["NOMOS_OFFLINE_RUNTIME"] || join2(homedir(), ".holon", "runtime");
|
|
4652
|
+
if (!deployPaths.length) die("no --deploy JSON path(s) given");
|
|
4653
|
+
if (!outPath) die("no --out snapshot path given");
|
|
4654
|
+
var CLOUD = (process.env["NOMOS_CLOUD"] || "https://nomos.captainapp.co.uk").replace(/\/+$/, "");
|
|
4655
|
+
var STALE_RUNTIME_HINT = "your cached kernel runtime may be stale \u2014 rm -rf ~/.holon/runtime and re-run (it re-fetches from the cloud)";
|
|
4656
|
+
var looksLikeStaleRuntime = (msg) => /\bbbox param\b|missing.*param|non-numeric|unknown (op|query op)|unsupported op|unexpected (field|key)|no such op/i.test(msg);
|
|
4657
|
+
await refreshRuntimeDir(CLOUD, runtimeDir, { log: (m) => process.stderr.write(m + "\n") });
|
|
4658
|
+
var wasmFile = join2(runtimeDir, "holon-testmint.wasm");
|
|
4659
|
+
var pkgsFile = join2(runtimeDir, "packages.json");
|
|
4660
|
+
if (!existsSync(wasmFile) || !existsSync(pkgsFile)) {
|
|
4661
|
+
die(`test runtime cache missing at ${runtimeDir} (holon-testmint.wasm + packages.json). Warm it once with a \`githolon\` run (it fetches + caches the runtime), then re-run offline.`);
|
|
4662
|
+
}
|
|
4663
|
+
var wasm = readFileSync(wasmFile);
|
|
4664
|
+
var pkgs = JSON.parse(readFileSync(pkgsFile, "utf8"));
|
|
4665
|
+
var wasmModule = await WebAssembly.compile(wasm);
|
|
4666
|
+
var replica = BigInt(`0x${randomBytes(8).toString("hex")}`) & (1n << 63n) - 1n;
|
|
4667
|
+
var eng = await createEngine2({ wasmModule, bootstrapPkg: pkgs.bootstrap, nomosPkg: pkgs.nomos, replica, installedBy: "nomos-test-harness" });
|
|
4668
|
+
await mountWorkspace2(eng, workspace, { remote: "https://unborn.invalid/", headers: {} });
|
|
4669
|
+
var guarded = (fn) => {
|
|
4670
|
+
try {
|
|
4671
|
+
return fn();
|
|
4672
|
+
} catch (e) {
|
|
4673
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
4674
|
+
if (looksLikeStaleRuntime(msg)) throw new Error(`${msg} \u2014 ${STALE_RUNTIME_HINT}`);
|
|
4675
|
+
throw e;
|
|
4676
|
+
}
|
|
4677
|
+
};
|
|
4678
|
+
var requireAdmission = (what, verdict) => {
|
|
4679
|
+
const result = verdict;
|
|
4680
|
+
if (result?.ok === true) return;
|
|
4681
|
+
die(`${what} was refused by the candidate kernel: ${String(result?.error ?? "no admission verdict")}`);
|
|
4682
|
+
};
|
|
4683
|
+
requireAdmission(
|
|
4684
|
+
"framework install",
|
|
4685
|
+
guarded(() => author2(eng, workspace, "bootstrap", "installDomain", installPayload2(eng.hashes.nomos, eng.nomosPkg, "nomos-test-harness"), ""))
|
|
4686
|
+
);
|
|
4687
|
+
var lawHashes = [];
|
|
4688
|
+
var expectedDomainLaw = /* @__PURE__ */ new Map();
|
|
4689
|
+
for (const p of deployPaths) {
|
|
4690
|
+
if (!existsSync(p)) die(`deploy JSON not found: ${p} \u2014 run \`npx githolon compile\` first`);
|
|
4691
|
+
const deploy2 = JSON.parse(readFileSync(p, "utf8"));
|
|
4692
|
+
const usda = deploy2.packageUsda;
|
|
4693
|
+
if (typeof usda !== "string" || !usda.startsWith("#usda")) die(`${p} has no #usda packageUsda`);
|
|
4694
|
+
const lawHash = await sha256hex2(usda);
|
|
4695
|
+
requireAdmission(
|
|
4696
|
+
`application install from ${p}`,
|
|
4697
|
+
guarded(() => author2(eng, workspace, "nomos", "installDomain", installPayload2(lawHash, usda, "nomos-test-harness"), eng.hashes.nomos))
|
|
4698
|
+
);
|
|
4699
|
+
lawHashes.push(lawHash);
|
|
4700
|
+
for (const declared of deploy2?.summary?.domains ?? []) {
|
|
4701
|
+
if (typeof declared?.domain === "string") expectedDomainLaw.set(declared.domain, lawHash);
|
|
4702
|
+
}
|
|
4703
|
+
process.stderr.write(`offline-install: installed ${p} (law ${lawHash.slice(0, 12)}\u2026)
|
|
4704
|
+
`);
|
|
4705
|
+
}
|
|
4706
|
+
var wsNode = eng.preopen.dir.contents.get("ws").contents.get(workspace);
|
|
4707
|
+
if (!wsNode) die(`internal: workspace tree '${workspace}' not found after install`);
|
|
4708
|
+
guarded(() => refreshProjection2(eng, workspace));
|
|
4709
|
+
var activeLaw = guarded(() => currentLaw2(eng, workspace));
|
|
4710
|
+
for (const [domain, expected] of expectedDomainLaw) {
|
|
4711
|
+
if (activeLaw?.[domain] !== expected) {
|
|
4712
|
+
die(`installed package did not become current for domain '${domain}': expected ${expected}, received ${activeLaw?.[domain] ?? "(none)"}`);
|
|
4713
|
+
}
|
|
4714
|
+
}
|
|
4715
|
+
var treeBytes = serializeTree2(wsNode);
|
|
4716
|
+
var checkpoint = guarded(() => exportCheckpoint2(eng, workspace));
|
|
4717
|
+
var snapshotBytes = new TextEncoder().encode(JSON.stringify({
|
|
4718
|
+
v: 2,
|
|
4719
|
+
tree: Buffer.from(treeBytes).toString("base64"),
|
|
4720
|
+
ckpt: typeof checkpoint?.blob === "string" ? checkpoint.blob : null,
|
|
4721
|
+
frontier: typeof checkpoint?.frontier === "string" ? checkpoint.frontier : null,
|
|
4722
|
+
cursor: checkpoint?.cursor ?? null,
|
|
4723
|
+
syncedBase: null
|
|
4724
|
+
}));
|
|
4725
|
+
writeFileSync2(outPath, snapshotBytes);
|
|
4726
|
+
process.stderr.write(`offline-install: application-ready snapshot written (${snapshotBytes.length} bytes) \u2192 ${outPath}
|
|
4727
|
+
`);
|
|
4728
|
+
var harnessDevice = authorKeygen2(eng);
|
|
4729
|
+
process.stdout.write(JSON.stringify({ ok: true, snapshot: outPath, workspace, lawHashes, frameworkHash: eng.hashes.nomos, devicePublic: harnessDevice.public, deviceSecret: harnessDevice.secret }) + "\n");
|