memeloop 0.2.8 → 0.2.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-NVJCRIEK.js → chunk-34B7MUI6.js} +2 -2
- package/dist/{chunk-DACM5AMS.js → chunk-7W4FMKMO.js} +190 -56
- package/dist/chunk-7W4FMKMO.js.map +1 -0
- package/dist/{chunk-OD6U6VAR.js → chunk-GH5RDPLJ.js} +2 -2
- package/dist/{chunk-OD6U6VAR.js.map → chunk-GH5RDPLJ.js.map} +1 -1
- package/dist/device-network/cloudDeviceFetchClient.d.ts +9 -7
- package/dist/device-network/cloudDeviceFetchClient.d.ts.map +1 -1
- package/dist/device-network/deviceCloudConnectionCoordinator.d.ts +5 -5
- package/dist/device-network/deviceCloudConnectionCoordinator.d.ts.map +1 -1
- package/dist/device-network/deviceOrchestrationTransport.d.ts +7 -0
- package/dist/device-network/deviceOrchestrationTransport.d.ts.map +1 -1
- package/dist/device-network/standardDeviceCloudConnectionAdapter.d.ts +16 -8
- package/dist/device-network/standardDeviceCloudConnectionAdapter.d.ts.map +1 -1
- package/dist/device-network/types.d.ts +4 -2
- package/dist/device-network/types.d.ts.map +1 -1
- package/dist/device-network-portable.cjs +190 -55
- package/dist/device-network-portable.cjs.map +1 -1
- package/dist/device-network-portable.js +189 -55
- package/dist/device-network-portable.js.map +1 -1
- package/dist/device-network.cjs +190 -55
- package/dist/device-network.cjs.map +1 -1
- package/dist/device-network.js +4 -2
- package/dist/index.cjs +488 -180
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +302 -127
- package/dist/index.js.map +1 -1
- package/dist/loop-api.cjs +1 -1
- package/dist/loop-api.cjs.map +1 -1
- package/dist/loop-api.js +1 -1
- package/dist/mobile.cjs.map +1 -1
- package/dist/orchestration/externalOrchestrationController.d.ts.map +1 -1
- package/dist/orchestration/workloadExecutionController.d.ts.map +1 -1
- package/dist/plugin/loader.d.ts +1 -1
- package/package.json +2 -1
- package/dist/chunk-DACM5AMS.js.map +0 -1
- /package/dist/{chunk-NVJCRIEK.js.map → chunk-34B7MUI6.js.map} +0 -0
package/dist/index.cjs
CHANGED
|
@@ -16496,10 +16496,54 @@ function createExternalOrchestrationController(store, options) {
|
|
|
16496
16496
|
const now = options.now ?? (() => /* @__PURE__ */ new Date());
|
|
16497
16497
|
const onError = options.onError ?? (() => {
|
|
16498
16498
|
});
|
|
16499
|
-
const active = /* @__PURE__ */ new
|
|
16499
|
+
const active = /* @__PURE__ */ new Map();
|
|
16500
16500
|
const activeByDriver = /* @__PURE__ */ new Map();
|
|
16501
16501
|
const watchAbort = new AbortController();
|
|
16502
|
+
const stoppedError = new OrchestrationError({
|
|
16503
|
+
code: "CANCELLED",
|
|
16504
|
+
message: "external orchestration controller stopped",
|
|
16505
|
+
retryable: false
|
|
16506
|
+
});
|
|
16507
|
+
let resolveStopped;
|
|
16508
|
+
const stoppedBoundary = new Promise((resolve) => {
|
|
16509
|
+
resolveStopped = resolve;
|
|
16510
|
+
});
|
|
16502
16511
|
let stopped = false;
|
|
16512
|
+
let stopPromise;
|
|
16513
|
+
function throwIfStopped() {
|
|
16514
|
+
if (stopped) throw stoppedError;
|
|
16515
|
+
}
|
|
16516
|
+
async function whileRunning(operation) {
|
|
16517
|
+
throwIfStopped();
|
|
16518
|
+
const operationResult = Promise.resolve().then(operation).then(
|
|
16519
|
+
(value) => ({ kind: "value", value }),
|
|
16520
|
+
(error2) => ({
|
|
16521
|
+
kind: "error",
|
|
16522
|
+
error: error2 instanceof Error ? error2 : new Error(safeErrorMessageFromUnknown(error2))
|
|
16523
|
+
})
|
|
16524
|
+
);
|
|
16525
|
+
const result = await Promise.race([operationResult, stoppedBoundary]);
|
|
16526
|
+
if (result.kind === "stopped") throw stoppedError;
|
|
16527
|
+
if (result.kind === "error") throw result.error;
|
|
16528
|
+
return result.value;
|
|
16529
|
+
}
|
|
16530
|
+
function reportUnlessStopped(error2) {
|
|
16531
|
+
if (!stopped && error2 !== stoppedError) onError(error2);
|
|
16532
|
+
}
|
|
16533
|
+
async function drainWithDeadline(tasks) {
|
|
16534
|
+
let timeout;
|
|
16535
|
+
try {
|
|
16536
|
+
await Promise.race([
|
|
16537
|
+
Promise.allSettled(tasks).then(() => {
|
|
16538
|
+
}),
|
|
16539
|
+
new Promise((resolve) => {
|
|
16540
|
+
timeout = setTimeout(resolve, 1e3);
|
|
16541
|
+
})
|
|
16542
|
+
]);
|
|
16543
|
+
} finally {
|
|
16544
|
+
if (timeout) clearTimeout(timeout);
|
|
16545
|
+
}
|
|
16546
|
+
}
|
|
16503
16547
|
async function acquireDriverSlot(entry) {
|
|
16504
16548
|
const limit = entry.capabilities.maxConcurrency;
|
|
16505
16549
|
while (!stopped) {
|
|
@@ -16508,7 +16552,7 @@ function createExternalOrchestrationController(store, options) {
|
|
|
16508
16552
|
activeByDriver.set(entry.name, current + 1);
|
|
16509
16553
|
return true;
|
|
16510
16554
|
}
|
|
16511
|
-
await sleep(pollIntervalMs);
|
|
16555
|
+
await whileRunning(() => sleep(pollIntervalMs));
|
|
16512
16556
|
}
|
|
16513
16557
|
return false;
|
|
16514
16558
|
}
|
|
@@ -16538,7 +16582,7 @@ function createExternalOrchestrationController(store, options) {
|
|
|
16538
16582
|
}
|
|
16539
16583
|
async function ensureRun(workload) {
|
|
16540
16584
|
const reference = runReferenceOf(workload);
|
|
16541
|
-
if (await store.get(reference)) return reference;
|
|
16585
|
+
if (await whileRunning(() => store.get(reference))) return reference;
|
|
16542
16586
|
const manifest2 = createAgentRunManifest(reference.name, {
|
|
16543
16587
|
workloadRef: {
|
|
16544
16588
|
apiVersion: workload.apiVersion,
|
|
@@ -16550,7 +16594,7 @@ function createExternalOrchestrationController(store, options) {
|
|
|
16550
16594
|
});
|
|
16551
16595
|
manifest2.metadata.namespace = workload.metadata.namespace;
|
|
16552
16596
|
try {
|
|
16553
|
-
await store.create(options.actor, manifest2);
|
|
16597
|
+
await whileRunning(() => store.create(options.actor, manifest2));
|
|
16554
16598
|
} catch (error2) {
|
|
16555
16599
|
if (!(error2 instanceof OrchestrationError) || error2.code !== "CONFLICT") throw error2;
|
|
16556
16600
|
}
|
|
@@ -16558,12 +16602,14 @@ function createExternalOrchestrationController(store, options) {
|
|
|
16558
16602
|
}
|
|
16559
16603
|
async function updateStatus(reference, patch) {
|
|
16560
16604
|
for (let attempt = 0; attempt < statusWriteAttempts; attempt += 1) {
|
|
16561
|
-
const current = await store.get(reference);
|
|
16605
|
+
const current = await whileRunning(() => store.get(reference));
|
|
16562
16606
|
if (!current) return;
|
|
16563
16607
|
try {
|
|
16564
|
-
await
|
|
16565
|
-
|
|
16566
|
-
|
|
16608
|
+
await whileRunning(
|
|
16609
|
+
() => store.updateStatus(options.actor, reference, patch(current.status), {
|
|
16610
|
+
resourceVersion: current.metadata.resourceVersion
|
|
16611
|
+
})
|
|
16612
|
+
);
|
|
16567
16613
|
return;
|
|
16568
16614
|
} catch (error2) {
|
|
16569
16615
|
if (error2 instanceof OrchestrationError && error2.code === "CONFLICT") continue;
|
|
@@ -16603,6 +16649,7 @@ function createExternalOrchestrationController(store, options) {
|
|
|
16603
16649
|
return entry;
|
|
16604
16650
|
}
|
|
16605
16651
|
async function fail5(resource, error2) {
|
|
16652
|
+
throwIfStopped();
|
|
16606
16653
|
const message = safeErrorMessageFromUnknown(error2, { fallback: "External orchestration failed" });
|
|
16607
16654
|
if (resource.kind === AGENT_WORKLOAD_KIND) {
|
|
16608
16655
|
await updateStatus(runReferenceOf(resource), () => ({
|
|
@@ -16675,6 +16722,7 @@ function createExternalOrchestrationController(store, options) {
|
|
|
16675
16722
|
return terminal;
|
|
16676
16723
|
}
|
|
16677
16724
|
async function reconcileWorkload(resource) {
|
|
16725
|
+
throwIfStopped();
|
|
16678
16726
|
const entry = findDriver(resource);
|
|
16679
16727
|
const runtime = resolveExternalWorkloadRuntime(
|
|
16680
16728
|
resource,
|
|
@@ -16683,7 +16731,7 @@ function createExternalOrchestrationController(store, options) {
|
|
|
16683
16731
|
resolveExternalWorkloadResources(resource, runtime);
|
|
16684
16732
|
const reference = referenceOf(resource);
|
|
16685
16733
|
const runReference = await ensureRun(resource);
|
|
16686
|
-
const latest = await store.get(reference);
|
|
16734
|
+
const latest = await whileRunning(() => store.get(reference));
|
|
16687
16735
|
let externalId = latest?.status?.externalId ?? resource.status?.externalId;
|
|
16688
16736
|
if (!externalId) {
|
|
16689
16737
|
if (!options.authorizeWorkloadPlacement) {
|
|
@@ -16693,8 +16741,8 @@ function createExternalOrchestrationController(store, options) {
|
|
|
16693
16741
|
retryable: false
|
|
16694
16742
|
});
|
|
16695
16743
|
}
|
|
16696
|
-
const authorization = await options.authorizeWorkloadPlacement(resource, entry);
|
|
16697
|
-
const health = await entry.driver.getHealth();
|
|
16744
|
+
const authorization = await whileRunning(() => options.authorizeWorkloadPlacement(resource, entry, watchAbort.signal));
|
|
16745
|
+
const health = await whileRunning(() => entry.driver.getHealth());
|
|
16698
16746
|
if (!health.healthy) {
|
|
16699
16747
|
throw new OrchestrationError({ code: "UNAVAILABLE", message: `external orchestrator '${entry.name}' is unhealthy`, retryable: true });
|
|
16700
16748
|
}
|
|
@@ -16707,21 +16755,25 @@ function createExternalOrchestrationController(store, options) {
|
|
|
16707
16755
|
placementPolicyDigest: authorization.policyDigest
|
|
16708
16756
|
}));
|
|
16709
16757
|
let scriptSource;
|
|
16710
|
-
|
|
16711
|
-
|
|
16758
|
+
const scriptReference = resource.spec.scriptReference;
|
|
16759
|
+
if (scriptReference) {
|
|
16760
|
+
scriptSource = await whileRunning(async () => options.resolveScriptSource?.(scriptReference));
|
|
16712
16761
|
if (scriptSource === void 0) {
|
|
16713
16762
|
throw new OrchestrationError({
|
|
16714
16763
|
code: "NOT_FOUND",
|
|
16715
|
-
message: `script artifact '${
|
|
16764
|
+
message: `script artifact '${scriptReference}' is unavailable for external placement`,
|
|
16716
16765
|
retryable: false
|
|
16717
16766
|
});
|
|
16718
16767
|
}
|
|
16719
16768
|
}
|
|
16720
|
-
const workerBootstrap = await options.createWorkerBootstrap?.(resource, runReference);
|
|
16721
|
-
const placed = await
|
|
16722
|
-
|
|
16723
|
-
|
|
16724
|
-
|
|
16769
|
+
const workerBootstrap = await whileRunning(async () => options.createWorkerBootstrap?.(resource, runReference));
|
|
16770
|
+
const placed = await whileRunning(
|
|
16771
|
+
() => entry.driver.placeWorkload(resource, options.actor, {
|
|
16772
|
+
...scriptSource !== void 0 ? { scriptSource } : {},
|
|
16773
|
+
...workerBootstrap !== void 0 ? { workerBootstrap } : {},
|
|
16774
|
+
signal: watchAbort.signal
|
|
16775
|
+
})
|
|
16776
|
+
);
|
|
16725
16777
|
externalId = placed.externalId;
|
|
16726
16778
|
await updateStatus(reference, (current) => ({
|
|
16727
16779
|
...current,
|
|
@@ -16735,12 +16787,13 @@ function createExternalOrchestrationController(store, options) {
|
|
|
16735
16787
|
await updateStatus(runReference, () => ({ phase: "Running" }));
|
|
16736
16788
|
}
|
|
16737
16789
|
while (!stopped) {
|
|
16738
|
-
const external = await entry.driver.getWorkloadStatus(externalId);
|
|
16790
|
+
const external = await whileRunning(() => entry.driver.getWorkloadStatus(externalId));
|
|
16739
16791
|
if (await reflectWorkloadStatus(resource, external)) return;
|
|
16740
|
-
await sleep(pollIntervalMs);
|
|
16792
|
+
await whileRunning(() => sleep(pollIntervalMs));
|
|
16741
16793
|
}
|
|
16742
16794
|
}
|
|
16743
16795
|
async function reconcileToolOperation(resource) {
|
|
16796
|
+
throwIfStopped();
|
|
16744
16797
|
const entry = findDriver(resource);
|
|
16745
16798
|
const runtimeImage = resource.metadata.annotations?.["memeloop.io/runtime-image"];
|
|
16746
16799
|
const contract = assertExternalToolOperationContract(
|
|
@@ -16749,10 +16802,10 @@ function createExternalOrchestrationController(store, options) {
|
|
|
16749
16802
|
runtimeImage
|
|
16750
16803
|
);
|
|
16751
16804
|
const reference = referenceOf(resource);
|
|
16752
|
-
const latest = await store.get(reference);
|
|
16805
|
+
const latest = await whileRunning(() => store.get(reference));
|
|
16753
16806
|
let externalId = latest?.status?.externalId ?? resource.status?.externalId;
|
|
16754
16807
|
if (!externalId) {
|
|
16755
|
-
const health = await entry.driver.getHealth();
|
|
16808
|
+
const health = await whileRunning(() => entry.driver.getHealth());
|
|
16756
16809
|
if (!health.healthy) {
|
|
16757
16810
|
throw new OrchestrationError({ code: "UNAVAILABLE", message: `external orchestrator '${entry.name}' is unhealthy`, retryable: true });
|
|
16758
16811
|
}
|
|
@@ -16768,14 +16821,14 @@ function createExternalOrchestrationController(store, options) {
|
|
|
16768
16821
|
retryable: false
|
|
16769
16822
|
});
|
|
16770
16823
|
}
|
|
16771
|
-
const authorization = await options.authorizeToolOperation(resource);
|
|
16824
|
+
const authorization = await whileRunning(() => options.authorizeToolOperation(resource, watchAbort.signal));
|
|
16772
16825
|
if (authorization.approval) {
|
|
16773
16826
|
await updateStatus(reference, (current) => ({
|
|
16774
16827
|
...current,
|
|
16775
16828
|
approval: authorization.approval
|
|
16776
16829
|
}));
|
|
16777
16830
|
}
|
|
16778
|
-
const placed = await entry.driver.executeToolOperation(resource, options.actor);
|
|
16831
|
+
const placed = await whileRunning(() => entry.driver.executeToolOperation(resource, options.actor));
|
|
16779
16832
|
externalId = placed.externalId;
|
|
16780
16833
|
await updateStatus(reference, (current) => ({
|
|
16781
16834
|
...current,
|
|
@@ -16788,27 +16841,28 @@ function createExternalOrchestrationController(store, options) {
|
|
|
16788
16841
|
}));
|
|
16789
16842
|
}
|
|
16790
16843
|
while (!stopped) {
|
|
16791
|
-
const external = await entry.driver.getToolOperationStatus(externalId);
|
|
16844
|
+
const external = await whileRunning(() => entry.driver.getToolOperationStatus(externalId));
|
|
16792
16845
|
if (await reflectToolStatus(resource, external, contract)) return;
|
|
16793
|
-
await sleep(pollIntervalMs);
|
|
16846
|
+
await whileRunning(() => sleep(pollIntervalMs));
|
|
16794
16847
|
}
|
|
16795
16848
|
}
|
|
16796
16849
|
function maybeStart(resource) {
|
|
16850
|
+
if (stopped) return;
|
|
16797
16851
|
const routed = resource;
|
|
16798
16852
|
if (!routeOf(routed)) return;
|
|
16799
16853
|
const phase = routed.status?.phase;
|
|
16800
16854
|
if (phase === "Completed" || phase === "Failed" || phase === "Cancelled") return;
|
|
16801
16855
|
if (active.has(routed.metadata.uid)) return;
|
|
16802
|
-
|
|
16803
|
-
void (async () => {
|
|
16856
|
+
const task = (async () => {
|
|
16804
16857
|
let slot;
|
|
16805
16858
|
try {
|
|
16806
16859
|
const entry = findDriver(routed);
|
|
16807
16860
|
if (!await acquireDriverSlot(entry)) return;
|
|
16808
16861
|
slot = entry.name;
|
|
16809
16862
|
} catch (error2) {
|
|
16863
|
+
if (stopped || error2 === stoppedError) return;
|
|
16810
16864
|
onError(error2);
|
|
16811
|
-
await fail5(routed, error2).catch(
|
|
16865
|
+
await fail5(routed, error2).catch(reportUnlessStopped);
|
|
16812
16866
|
return;
|
|
16813
16867
|
}
|
|
16814
16868
|
try {
|
|
@@ -16821,18 +16875,25 @@ function createExternalOrchestrationController(store, options) {
|
|
|
16821
16875
|
}
|
|
16822
16876
|
return;
|
|
16823
16877
|
} catch (error2) {
|
|
16878
|
+
if (stopped || error2 === stoppedError) return;
|
|
16824
16879
|
onError(error2);
|
|
16825
16880
|
if (!(error2 instanceof OrchestrationError) || !error2.retryable) {
|
|
16826
|
-
await fail5(routed, error2).catch(
|
|
16881
|
+
await fail5(routed, error2).catch(reportUnlessStopped);
|
|
16827
16882
|
return;
|
|
16828
16883
|
}
|
|
16829
|
-
await sleep(pollIntervalMs);
|
|
16884
|
+
await whileRunning(() => sleep(pollIntervalMs));
|
|
16830
16885
|
}
|
|
16831
16886
|
}
|
|
16832
16887
|
} finally {
|
|
16833
16888
|
if (slot) releaseDriverSlot(slot);
|
|
16834
16889
|
}
|
|
16835
|
-
})()
|
|
16890
|
+
})();
|
|
16891
|
+
active.set(routed.metadata.uid, task);
|
|
16892
|
+
void task.finally(() => {
|
|
16893
|
+
if (active.get(routed.metadata.uid) === task) active.delete(routed.metadata.uid);
|
|
16894
|
+
}).catch((error2) => {
|
|
16895
|
+
if (!stopped && error2 !== stoppedError) onError(error2);
|
|
16896
|
+
});
|
|
16836
16897
|
}
|
|
16837
16898
|
async function cancelDeleted(resource) {
|
|
16838
16899
|
const driverName = resource.status?.assignedDriver ?? routeOf(resource);
|
|
@@ -16853,7 +16914,7 @@ function createExternalOrchestrationController(store, options) {
|
|
|
16853
16914
|
{ apiVersion, kind },
|
|
16854
16915
|
{ signal: watchAbort.signal }
|
|
16855
16916
|
)[Symbol.asyncIterator]();
|
|
16856
|
-
const existing = await store.list({ apiVersion, kind });
|
|
16917
|
+
const existing = await whileRunning(() => store.list({ apiVersion, kind }));
|
|
16857
16918
|
for (const resource of existing.items) maybeStart(resource);
|
|
16858
16919
|
while (!stopped) {
|
|
16859
16920
|
const next = await iterator.next();
|
|
@@ -16861,30 +16922,35 @@ function createExternalOrchestrationController(store, options) {
|
|
|
16861
16922
|
const event = next.value;
|
|
16862
16923
|
if (stopped) break;
|
|
16863
16924
|
if (event.type === "DELETED") {
|
|
16864
|
-
await cancelDeleted(event.resource).catch(
|
|
16925
|
+
await cancelDeleted(event.resource).catch(reportUnlessStopped);
|
|
16865
16926
|
} else if (event.type === "ADDED" || event.type === "MODIFIED") {
|
|
16866
16927
|
maybeStart(event.resource);
|
|
16867
16928
|
}
|
|
16868
16929
|
}
|
|
16869
16930
|
} catch (error2) {
|
|
16870
|
-
onError(error2);
|
|
16931
|
+
if (!stopped && error2 !== stoppedError) onError(error2);
|
|
16932
|
+
}
|
|
16933
|
+
if (!stopped) {
|
|
16934
|
+
await whileRunning(() => sleep(1e3)).catch((error2) => {
|
|
16935
|
+
if (!stopped && error2 !== stoppedError) onError(error2);
|
|
16936
|
+
});
|
|
16871
16937
|
}
|
|
16872
|
-
if (!stopped) await sleep(1e3);
|
|
16873
16938
|
}
|
|
16874
16939
|
}
|
|
16875
16940
|
const watchers = [
|
|
16876
16941
|
runKind(AGENT_WORKLOAD_API_VERSION, AGENT_WORKLOAD_KIND),
|
|
16877
16942
|
runKind(TOOL_OPERATION_API_VERSION, TOOL_OPERATION_KIND)
|
|
16878
16943
|
];
|
|
16879
|
-
for (const watcher of watchers) void watcher.catch(
|
|
16944
|
+
for (const watcher of watchers) void watcher.catch(reportUnlessStopped);
|
|
16880
16945
|
return {
|
|
16881
16946
|
async stop() {
|
|
16882
|
-
|
|
16883
|
-
|
|
16884
|
-
|
|
16885
|
-
|
|
16886
|
-
|
|
16887
|
-
|
|
16947
|
+
if (!stopPromise) {
|
|
16948
|
+
stopped = true;
|
|
16949
|
+
resolveStopped({ kind: "stopped" });
|
|
16950
|
+
watchAbort.abort(stoppedError);
|
|
16951
|
+
stopPromise = drainWithDeadline([...watchers, ...active.values()]);
|
|
16952
|
+
}
|
|
16953
|
+
await stopPromise;
|
|
16888
16954
|
}
|
|
16889
16955
|
};
|
|
16890
16956
|
}
|
|
@@ -37656,15 +37722,64 @@ function createWorkloadExecutionController(store, driver, options) {
|
|
|
37656
37722
|
});
|
|
37657
37723
|
}
|
|
37658
37724
|
const active = /* @__PURE__ */ new Map();
|
|
37725
|
+
const executions = /* @__PURE__ */ new Map();
|
|
37726
|
+
const cancellationRequested = /* @__PURE__ */ new Set();
|
|
37727
|
+
const watchAbort = new AbortController();
|
|
37728
|
+
const stoppedError = new OrchestrationError({
|
|
37729
|
+
code: "CANCELLED",
|
|
37730
|
+
message: "workload execution controller stopped",
|
|
37731
|
+
retryable: false
|
|
37732
|
+
});
|
|
37733
|
+
let resolveStopped;
|
|
37734
|
+
const stoppedBoundary = new Promise((resolve) => {
|
|
37735
|
+
resolveStopped = resolve;
|
|
37736
|
+
});
|
|
37659
37737
|
let stopped = false;
|
|
37738
|
+
let stopPromise;
|
|
37739
|
+
function throwIfStopped() {
|
|
37740
|
+
if (stopped) throw stoppedError;
|
|
37741
|
+
}
|
|
37742
|
+
async function whileRunning(operation) {
|
|
37743
|
+
throwIfStopped();
|
|
37744
|
+
const operationResult = Promise.resolve().then(operation).then(
|
|
37745
|
+
(value) => ({ kind: "value", value }),
|
|
37746
|
+
(error2) => ({
|
|
37747
|
+
kind: "error",
|
|
37748
|
+
error: error2 instanceof Error ? error2 : new Error(safeErrorMessageFromUnknown(error2))
|
|
37749
|
+
})
|
|
37750
|
+
);
|
|
37751
|
+
const result = await Promise.race([operationResult, stoppedBoundary]);
|
|
37752
|
+
if (result.kind === "stopped") throw stoppedError;
|
|
37753
|
+
if (result.kind === "error") throw result.error;
|
|
37754
|
+
return result.value;
|
|
37755
|
+
}
|
|
37756
|
+
function reportUnlessStopped(error2) {
|
|
37757
|
+
if (!stopped && error2 !== stoppedError) onError(error2);
|
|
37758
|
+
}
|
|
37759
|
+
async function drainWithDeadline(tasks) {
|
|
37760
|
+
let timeout;
|
|
37761
|
+
try {
|
|
37762
|
+
await Promise.race([
|
|
37763
|
+
Promise.allSettled(tasks).then(() => {
|
|
37764
|
+
}),
|
|
37765
|
+
new Promise((resolve) => {
|
|
37766
|
+
timeout = setTimeout(resolve, 1e3);
|
|
37767
|
+
})
|
|
37768
|
+
]);
|
|
37769
|
+
} finally {
|
|
37770
|
+
if (timeout) clearTimeout(timeout);
|
|
37771
|
+
}
|
|
37772
|
+
}
|
|
37660
37773
|
async function updateStatusWithRetry(reference, patch) {
|
|
37661
37774
|
for (let attempt = 0; attempt < statusWriteAttempts; attempt += 1) {
|
|
37662
|
-
const current = await store.get(reference);
|
|
37775
|
+
const current = await whileRunning(() => store.get(reference));
|
|
37663
37776
|
if (!current) return;
|
|
37664
37777
|
try {
|
|
37665
|
-
await
|
|
37666
|
-
|
|
37667
|
-
|
|
37778
|
+
await whileRunning(
|
|
37779
|
+
() => store.updateStatus(options.actor, reference, patch(current.status), {
|
|
37780
|
+
resourceVersion: current.metadata.resourceVersion
|
|
37781
|
+
})
|
|
37782
|
+
);
|
|
37668
37783
|
return;
|
|
37669
37784
|
} catch (error2) {
|
|
37670
37785
|
if (error2 instanceof OrchestrationError && error2.code === "CONFLICT") continue;
|
|
@@ -37699,22 +37814,26 @@ function createWorkloadExecutionController(store, driver, options) {
|
|
|
37699
37814
|
}
|
|
37700
37815
|
async function claimRuntimeExecution(reference) {
|
|
37701
37816
|
for (let attempt = 0; attempt < statusWriteAttempts; attempt += 1) {
|
|
37702
|
-
const current = await
|
|
37817
|
+
const current = await whileRunning(
|
|
37818
|
+
() => store.get(reference)
|
|
37819
|
+
);
|
|
37703
37820
|
if (!current) return null;
|
|
37704
37821
|
if (current.status?.runtimeExecutionClaim) return null;
|
|
37705
37822
|
try {
|
|
37706
|
-
return await
|
|
37707
|
-
|
|
37708
|
-
|
|
37709
|
-
|
|
37710
|
-
|
|
37711
|
-
|
|
37712
|
-
|
|
37713
|
-
|
|
37714
|
-
|
|
37715
|
-
|
|
37716
|
-
|
|
37717
|
-
|
|
37823
|
+
return await whileRunning(
|
|
37824
|
+
() => store.updateStatus(
|
|
37825
|
+
options.actor,
|
|
37826
|
+
reference,
|
|
37827
|
+
{
|
|
37828
|
+
...current.status,
|
|
37829
|
+
phase: "Starting",
|
|
37830
|
+
runtimeExecutionClaim: {
|
|
37831
|
+
controllerInstanceId,
|
|
37832
|
+
claimedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
37833
|
+
}
|
|
37834
|
+
},
|
|
37835
|
+
{ resourceVersion: current.metadata.resourceVersion }
|
|
37836
|
+
)
|
|
37718
37837
|
);
|
|
37719
37838
|
} catch (error2) {
|
|
37720
37839
|
if (error2 instanceof OrchestrationError && error2.code === "CONFLICT") {
|
|
@@ -37739,8 +37858,10 @@ function createWorkloadExecutionController(store, driver, options) {
|
|
|
37739
37858
|
retryable: false
|
|
37740
37859
|
});
|
|
37741
37860
|
}
|
|
37742
|
-
const current = await
|
|
37743
|
-
|
|
37861
|
+
const current = await whileRunning(
|
|
37862
|
+
() => store.get(
|
|
37863
|
+
runReference
|
|
37864
|
+
)
|
|
37744
37865
|
);
|
|
37745
37866
|
if (!current) {
|
|
37746
37867
|
throw new OrchestrationError({
|
|
@@ -37752,12 +37873,14 @@ function createWorkloadExecutionController(store, driver, options) {
|
|
|
37752
37873
|
if (!workload.spec.modelPolicy?.modelClass) return { run: current };
|
|
37753
37874
|
const binding = current.status?.assignedModelEndpoint;
|
|
37754
37875
|
if (binding) {
|
|
37755
|
-
const endpoint = await
|
|
37756
|
-
|
|
37757
|
-
|
|
37758
|
-
|
|
37759
|
-
|
|
37760
|
-
|
|
37876
|
+
const endpoint = await whileRunning(
|
|
37877
|
+
() => store.get({
|
|
37878
|
+
apiVersion: binding.apiVersion || MODEL_ENDPOINT_API_VERSION,
|
|
37879
|
+
kind: binding.kind || MODEL_ENDPOINT_KIND,
|
|
37880
|
+
name: binding.name,
|
|
37881
|
+
namespace: binding.namespace
|
|
37882
|
+
})
|
|
37883
|
+
);
|
|
37761
37884
|
if (endpoint && endpoint.metadata.uid === binding.uid && endpoint.status?.healthy === true && Number.isFinite(Date.parse(endpoint.status.heartbeat ?? "")) && Date.now() - Date.parse(endpoint.status.heartbeat ?? "") <= modelEndpointHeartbeatTtlMs && endpoint.spec.modelClassRef.name === workload.spec.modelPolicy.modelClass) {
|
|
37762
37885
|
return { run: current, endpoint };
|
|
37763
37886
|
}
|
|
@@ -37769,7 +37892,7 @@ function createWorkloadExecutionController(store, driver, options) {
|
|
|
37769
37892
|
retryable: true
|
|
37770
37893
|
});
|
|
37771
37894
|
}
|
|
37772
|
-
await sleep(dependencyPollIntervalMs);
|
|
37895
|
+
await whileRunning(() => sleep(dependencyPollIntervalMs));
|
|
37773
37896
|
}
|
|
37774
37897
|
}
|
|
37775
37898
|
async function ensureNetworkAttachment(workload, runReference, runUid) {
|
|
@@ -37782,7 +37905,9 @@ function createWorkloadExecutionController(store, driver, options) {
|
|
|
37782
37905
|
name: attachmentName,
|
|
37783
37906
|
namespace: workload.metadata.namespace
|
|
37784
37907
|
};
|
|
37785
|
-
let attachment = await
|
|
37908
|
+
let attachment = await whileRunning(
|
|
37909
|
+
() => store.get(reference)
|
|
37910
|
+
);
|
|
37786
37911
|
if (!attachment) {
|
|
37787
37912
|
const manifest2 = createNetworkAttachmentManifest(attachmentName, {
|
|
37788
37913
|
networkClassRef: {
|
|
@@ -37806,7 +37931,7 @@ function createWorkloadExecutionController(store, driver, options) {
|
|
|
37806
37931
|
nodeId: options.nodeId
|
|
37807
37932
|
});
|
|
37808
37933
|
manifest2.metadata.namespace = workload.metadata.namespace;
|
|
37809
|
-
attachment = await store.create(options.actor, manifest2);
|
|
37934
|
+
attachment = await whileRunning(() => store.create(options.actor, manifest2));
|
|
37810
37935
|
}
|
|
37811
37936
|
await updateStatusWithRetry(runReference, (current) => ({
|
|
37812
37937
|
...current,
|
|
@@ -37827,7 +37952,9 @@ function createWorkloadExecutionController(store, driver, options) {
|
|
|
37827
37952
|
retryable: false
|
|
37828
37953
|
});
|
|
37829
37954
|
}
|
|
37830
|
-
const current = await
|
|
37955
|
+
const current = await whileRunning(
|
|
37956
|
+
() => store.get(reference)
|
|
37957
|
+
);
|
|
37831
37958
|
if (!current || current.metadata.uid !== attachment.metadata.uid) {
|
|
37832
37959
|
throw new OrchestrationError({
|
|
37833
37960
|
code: "NOT_FOUND",
|
|
@@ -37843,7 +37970,9 @@ function createWorkloadExecutionController(store, driver, options) {
|
|
|
37843
37970
|
});
|
|
37844
37971
|
}
|
|
37845
37972
|
if (current.status?.phase === "Attached" && current.status.handle) {
|
|
37846
|
-
const networkClass = await
|
|
37973
|
+
const networkClass = await whileRunning(
|
|
37974
|
+
() => store.get(current.spec.networkClassRef)
|
|
37975
|
+
);
|
|
37847
37976
|
if (networkClass && current.status.binding?.networkClassResourceVersion === networkClass.metadata.resourceVersion && current.status.assignedNode === options.nodeId && current.status.assignedDriver === networkClass.spec.driver) {
|
|
37848
37977
|
return current;
|
|
37849
37978
|
}
|
|
@@ -37855,14 +37984,16 @@ function createWorkloadExecutionController(store, driver, options) {
|
|
|
37855
37984
|
retryable: true
|
|
37856
37985
|
});
|
|
37857
37986
|
}
|
|
37858
|
-
await sleep(dependencyPollIntervalMs);
|
|
37987
|
+
await whileRunning(() => sleep(dependencyPollIntervalMs));
|
|
37859
37988
|
}
|
|
37860
37989
|
}
|
|
37861
37990
|
async function waitForVolumeBindings(workload, runReference) {
|
|
37862
37991
|
const expected = workload.spec.storagePolicy?.volumes ?? [];
|
|
37863
37992
|
if (expected.length === 0) {
|
|
37864
|
-
const run5 = await
|
|
37865
|
-
|
|
37993
|
+
const run5 = await whileRunning(
|
|
37994
|
+
() => store.get(
|
|
37995
|
+
runReference
|
|
37996
|
+
)
|
|
37866
37997
|
);
|
|
37867
37998
|
return { run: run5 };
|
|
37868
37999
|
}
|
|
@@ -37875,8 +38006,10 @@ function createWorkloadExecutionController(store, driver, options) {
|
|
|
37875
38006
|
retryable: false
|
|
37876
38007
|
});
|
|
37877
38008
|
}
|
|
37878
|
-
const run5 = await
|
|
37879
|
-
|
|
38009
|
+
const run5 = await whileRunning(
|
|
38010
|
+
() => store.get(
|
|
38011
|
+
runReference
|
|
38012
|
+
)
|
|
37880
38013
|
);
|
|
37881
38014
|
if (!run5) {
|
|
37882
38015
|
throw new OrchestrationError({
|
|
@@ -37903,7 +38036,7 @@ function createWorkloadExecutionController(store, driver, options) {
|
|
|
37903
38036
|
retryable: false
|
|
37904
38037
|
});
|
|
37905
38038
|
}
|
|
37906
|
-
return { run: run5, mounts: await options.resolveVolumeMounts(workload, run5) };
|
|
38039
|
+
return { run: run5, mounts: await whileRunning(() => options.resolveVolumeMounts(workload, run5)) };
|
|
37907
38040
|
}
|
|
37908
38041
|
if (Date.now() >= deadline) {
|
|
37909
38042
|
throw new OrchestrationError({
|
|
@@ -37912,12 +38045,14 @@ function createWorkloadExecutionController(store, driver, options) {
|
|
|
37912
38045
|
retryable: true
|
|
37913
38046
|
});
|
|
37914
38047
|
}
|
|
37915
|
-
await sleep(dependencyPollIntervalMs);
|
|
38048
|
+
await whileRunning(() => sleep(dependencyPollIntervalMs));
|
|
37916
38049
|
}
|
|
37917
38050
|
}
|
|
37918
38051
|
async function requestDependencyRelease(runReference) {
|
|
37919
|
-
const run5 = await
|
|
37920
|
-
|
|
38052
|
+
const run5 = await whileRunning(
|
|
38053
|
+
() => store.get(
|
|
38054
|
+
runReference
|
|
38055
|
+
)
|
|
37921
38056
|
);
|
|
37922
38057
|
if (!run5) return;
|
|
37923
38058
|
const requestedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -37944,6 +38079,7 @@ function createWorkloadExecutionController(store, driver, options) {
|
|
|
37944
38079
|
}
|
|
37945
38080
|
}
|
|
37946
38081
|
async function execute(workload) {
|
|
38082
|
+
throwIfStopped();
|
|
37947
38083
|
const workloadReference_ = workloadReference(workload);
|
|
37948
38084
|
const runName = `${workload.metadata.name}-run`;
|
|
37949
38085
|
const runReference = {
|
|
@@ -37955,18 +38091,19 @@ function createWorkloadExecutionController(store, driver, options) {
|
|
|
37955
38091
|
let runtimeClaimed = false;
|
|
37956
38092
|
try {
|
|
37957
38093
|
let scriptSource;
|
|
37958
|
-
|
|
37959
|
-
|
|
38094
|
+
const scriptReference = workload.spec.scriptReference;
|
|
38095
|
+
if (scriptReference) {
|
|
38096
|
+
scriptSource = await whileRunning(async () => options.resolveScriptSource?.(scriptReference));
|
|
37960
38097
|
if (!scriptSource) {
|
|
37961
38098
|
await updateStatusWithRetry(workloadReference_, (current) => ({
|
|
37962
38099
|
...current,
|
|
37963
38100
|
phase: "Failed",
|
|
37964
|
-
lastRunResult: `script artifact '${
|
|
38101
|
+
lastRunResult: `script artifact '${scriptReference}' unavailable`
|
|
37965
38102
|
}));
|
|
37966
38103
|
return;
|
|
37967
38104
|
}
|
|
37968
38105
|
}
|
|
37969
|
-
let run5 = await store.get(runReference);
|
|
38106
|
+
let run5 = await whileRunning(() => store.get(runReference));
|
|
37970
38107
|
if (!run5) {
|
|
37971
38108
|
const manifest2 = createAgentRunManifest(runName, {
|
|
37972
38109
|
workloadRef: {
|
|
@@ -37979,15 +38116,19 @@ function createWorkloadExecutionController(store, driver, options) {
|
|
|
37979
38116
|
});
|
|
37980
38117
|
manifest2.metadata.namespace = workload.metadata.namespace;
|
|
37981
38118
|
try {
|
|
37982
|
-
run5 = await
|
|
37983
|
-
|
|
37984
|
-
|
|
38119
|
+
run5 = await whileRunning(
|
|
38120
|
+
() => store.create(
|
|
38121
|
+
options.actor,
|
|
38122
|
+
manifest2
|
|
38123
|
+
)
|
|
37985
38124
|
);
|
|
37986
38125
|
} catch (error2) {
|
|
37987
38126
|
if (!(error2 instanceof OrchestrationError) || error2.code !== "CONFLICT") {
|
|
37988
38127
|
throw error2;
|
|
37989
38128
|
}
|
|
37990
|
-
run5 = await
|
|
38129
|
+
run5 = await whileRunning(
|
|
38130
|
+
() => store.get(runReference)
|
|
38131
|
+
);
|
|
37991
38132
|
if (!run5) throw error2;
|
|
37992
38133
|
}
|
|
37993
38134
|
}
|
|
@@ -38007,8 +38148,10 @@ function createWorkloadExecutionController(store, driver, options) {
|
|
|
38007
38148
|
waitForVolumeBindings(workload, runReference)
|
|
38008
38149
|
]);
|
|
38009
38150
|
run5 = volumeDependencies.run;
|
|
38010
|
-
run5 = await
|
|
38011
|
-
|
|
38151
|
+
run5 = await whileRunning(
|
|
38152
|
+
() => store.get(
|
|
38153
|
+
runReference
|
|
38154
|
+
)
|
|
38012
38155
|
) ?? run5;
|
|
38013
38156
|
await updateStatusWithRetry(workloadReference_, (current) => ({
|
|
38014
38157
|
...current,
|
|
@@ -38019,21 +38162,27 @@ function createWorkloadExecutionController(store, driver, options) {
|
|
|
38019
38162
|
if (!claimedRun) return;
|
|
38020
38163
|
runtimeClaimed = true;
|
|
38021
38164
|
run5 = claimedRun;
|
|
38022
|
-
const handle = await
|
|
38023
|
-
|
|
38024
|
-
|
|
38025
|
-
|
|
38026
|
-
|
|
38027
|
-
|
|
38028
|
-
|
|
38029
|
-
|
|
38030
|
-
|
|
38165
|
+
const handle = await whileRunning(
|
|
38166
|
+
() => driver.start({
|
|
38167
|
+
workload,
|
|
38168
|
+
run: run5,
|
|
38169
|
+
...dependencies.endpoint ? { modelEndpoint: dependencies.endpoint } : {},
|
|
38170
|
+
...networkAttachment ? { networkAttachment } : {},
|
|
38171
|
+
...volumeDependencies.mounts ? { volumeMounts: volumeDependencies.mounts } : {},
|
|
38172
|
+
scriptSource,
|
|
38173
|
+
message: options.messageForWorkload?.(workload) ?? workload.metadata.name
|
|
38174
|
+
})
|
|
38175
|
+
);
|
|
38031
38176
|
active.set(workload.metadata.uid, handle);
|
|
38177
|
+
if (cancellationRequested.has(workload.metadata.uid)) {
|
|
38178
|
+
await handle.cancel().catch(reportUnlessStopped);
|
|
38179
|
+
return;
|
|
38180
|
+
}
|
|
38032
38181
|
await updateStatusWithRetry(runReference, (current) => ({
|
|
38033
38182
|
...current,
|
|
38034
38183
|
phase: "Running"
|
|
38035
38184
|
}));
|
|
38036
|
-
const outcome = await handle.wait();
|
|
38185
|
+
const outcome = await whileRunning(() => handle.wait());
|
|
38037
38186
|
await requestDependencyRelease(runReference);
|
|
38038
38187
|
await updateStatusWithRetry(runReference, (current) => ({
|
|
38039
38188
|
...current,
|
|
@@ -38047,7 +38196,8 @@ function createWorkloadExecutionController(store, driver, options) {
|
|
|
38047
38196
|
lastRunResult: outcome.summary ?? outcome.error?.message
|
|
38048
38197
|
}));
|
|
38049
38198
|
} catch (error2) {
|
|
38050
|
-
|
|
38199
|
+
if (stopped || error2 === stoppedError) return;
|
|
38200
|
+
await requestDependencyRelease(runReference).catch(reportUnlessStopped);
|
|
38051
38201
|
const cause = safeErrorMessageFromUnknown(error2, { fallback: "Runtime execution failed" });
|
|
38052
38202
|
const message = runtimeClaimed ? `UNKNOWN_EFFECT: runtime execution failed after the durable pre-effect claim; verify external state before retrying: ${cause}` : cause;
|
|
38053
38203
|
await updateStatusWithRetry(runReference, (current) => ({
|
|
@@ -38057,19 +38207,20 @@ function createWorkloadExecutionController(store, driver, options) {
|
|
|
38057
38207
|
summary: message,
|
|
38058
38208
|
exitCode: 1
|
|
38059
38209
|
}
|
|
38060
|
-
})).catch(
|
|
38061
|
-
if (stopped) return;
|
|
38210
|
+
})).catch(reportUnlessStopped);
|
|
38062
38211
|
onError(error2);
|
|
38063
38212
|
await updateStatusWithRetry(workloadReference_, (current) => ({
|
|
38064
38213
|
...current,
|
|
38065
38214
|
phase: "Failed",
|
|
38066
38215
|
lastRunResult: message
|
|
38067
|
-
})).catch(
|
|
38216
|
+
})).catch(reportUnlessStopped);
|
|
38068
38217
|
} finally {
|
|
38069
38218
|
active.delete(workload.metadata.uid);
|
|
38219
|
+
cancellationRequested.delete(workload.metadata.uid);
|
|
38070
38220
|
}
|
|
38071
38221
|
}
|
|
38072
38222
|
async function recoverRunning(workload) {
|
|
38223
|
+
throwIfStopped();
|
|
38073
38224
|
const runReference = {
|
|
38074
38225
|
apiVersion: AGENT_RUN_API_VERSION,
|
|
38075
38226
|
kind: AGENT_RUN_KIND,
|
|
@@ -38077,8 +38228,10 @@ function createWorkloadExecutionController(store, driver, options) {
|
|
|
38077
38228
|
namespace: workload.metadata.namespace
|
|
38078
38229
|
};
|
|
38079
38230
|
try {
|
|
38080
|
-
const run5 = await
|
|
38081
|
-
|
|
38231
|
+
const run5 = await whileRunning(
|
|
38232
|
+
() => store.get(
|
|
38233
|
+
runReference
|
|
38234
|
+
)
|
|
38082
38235
|
);
|
|
38083
38236
|
if (run5) {
|
|
38084
38237
|
try {
|
|
@@ -38136,12 +38289,14 @@ function createWorkloadExecutionController(store, driver, options) {
|
|
|
38136
38289
|
})
|
|
38137
38290
|
);
|
|
38138
38291
|
} catch (error2) {
|
|
38139
|
-
onError(error2);
|
|
38292
|
+
if (!stopped && error2 !== stoppedError) onError(error2);
|
|
38140
38293
|
} finally {
|
|
38141
38294
|
active.delete(workload.metadata.uid);
|
|
38295
|
+
cancellationRequested.delete(workload.metadata.uid);
|
|
38142
38296
|
}
|
|
38143
38297
|
}
|
|
38144
38298
|
function maybeStart(resource) {
|
|
38299
|
+
if (stopped) return;
|
|
38145
38300
|
const workload = resource;
|
|
38146
38301
|
const status = workload.status;
|
|
38147
38302
|
if (!status || status.phase !== "Scheduling" && status.phase !== "Running") return;
|
|
@@ -38149,39 +38304,57 @@ function createWorkloadExecutionController(store, driver, options) {
|
|
|
38149
38304
|
if (active.has(workload.metadata.uid)) return;
|
|
38150
38305
|
active.set(workload.metadata.uid, { cancel: async () => {
|
|
38151
38306
|
} });
|
|
38152
|
-
|
|
38153
|
-
|
|
38154
|
-
|
|
38155
|
-
|
|
38156
|
-
|
|
38307
|
+
const execution = status.phase === "Running" ? recoverRunning(workload) : execute(workload);
|
|
38308
|
+
executions.set(workload.metadata.uid, execution);
|
|
38309
|
+
void execution.finally(() => {
|
|
38310
|
+
if (executions.get(workload.metadata.uid) === execution) {
|
|
38311
|
+
executions.delete(workload.metadata.uid);
|
|
38312
|
+
}
|
|
38313
|
+
}).catch((error2) => {
|
|
38314
|
+
if (!stopped && error2 !== stoppedError) onError(error2);
|
|
38315
|
+
});
|
|
38157
38316
|
}
|
|
38158
|
-
|
|
38317
|
+
const watcher = (async () => {
|
|
38159
38318
|
while (!stopped) {
|
|
38160
38319
|
try {
|
|
38161
38320
|
for await (const event of store.watch(
|
|
38162
38321
|
{ apiVersion: AGENT_WORKLOAD_API_VERSION, kind: AGENT_WORKLOAD_KIND },
|
|
38163
|
-
{ sendInitialEvents: true }
|
|
38322
|
+
{ sendInitialEvents: true, signal: watchAbort.signal }
|
|
38164
38323
|
)) {
|
|
38165
38324
|
if (stopped) break;
|
|
38166
38325
|
if (event.type === "ADDED" || event.type === "MODIFIED") {
|
|
38167
38326
|
maybeStart(event.resource);
|
|
38168
38327
|
} else if (event.type === "DELETED") {
|
|
38169
38328
|
const workload = event.resource;
|
|
38170
|
-
|
|
38329
|
+
cancellationRequested.add(workload.metadata.uid);
|
|
38330
|
+
await active.get(workload.metadata.uid)?.cancel().catch(reportUnlessStopped);
|
|
38171
38331
|
}
|
|
38172
38332
|
}
|
|
38173
38333
|
} catch (error2) {
|
|
38174
|
-
onError(error2);
|
|
38334
|
+
if (!stopped && error2 !== stoppedError) onError(error2);
|
|
38335
|
+
}
|
|
38336
|
+
if (!stopped) {
|
|
38337
|
+
await whileRunning(() => sleep(1e3)).catch((error2) => {
|
|
38338
|
+
if (!stopped && error2 !== stoppedError) onError(error2);
|
|
38339
|
+
});
|
|
38175
38340
|
}
|
|
38176
|
-
if (!stopped) await sleep(1e3);
|
|
38177
38341
|
}
|
|
38178
|
-
})()
|
|
38342
|
+
})();
|
|
38343
|
+
void watcher.catch((error2) => {
|
|
38344
|
+
if (!stopped && error2 !== stoppedError) onError(error2);
|
|
38345
|
+
});
|
|
38179
38346
|
return {
|
|
38180
38347
|
async stop() {
|
|
38181
|
-
|
|
38182
|
-
|
|
38183
|
-
|
|
38348
|
+
if (!stopPromise) {
|
|
38349
|
+
stopped = true;
|
|
38350
|
+
resolveStopped({ kind: "stopped" });
|
|
38351
|
+
watchAbort.abort(stoppedError);
|
|
38352
|
+
const cancellations = [...active.values()].map(async (handle) => {
|
|
38353
|
+
await handle.cancel().catch(() => void 0);
|
|
38354
|
+
});
|
|
38355
|
+
stopPromise = drainWithDeadline([...cancellations, watcher, ...executions.values()]);
|
|
38184
38356
|
}
|
|
38357
|
+
await stopPromise;
|
|
38185
38358
|
}
|
|
38186
38359
|
};
|
|
38187
38360
|
}
|
|
@@ -38955,6 +39128,7 @@ __export(src_exports, {
|
|
|
38955
39128
|
DEVICE_HEARTBEAT_DEFAULT_MAX_CLOCK_SKEW_MS: () => DEVICE_HEARTBEAT_DEFAULT_MAX_CLOCK_SKEW_MS,
|
|
38956
39129
|
DEVICE_HEARTBEAT_LIMITS: () => DEVICE_HEARTBEAT_LIMITS,
|
|
38957
39130
|
DEVICE_HEARTBEAT_SIGNATURE_DOMAIN: () => DEVICE_HEARTBEAT_SIGNATURE_DOMAIN,
|
|
39131
|
+
DEVICE_ORCHESTRATION_FRAME_LIMITS: () => DEVICE_ORCHESTRATION_FRAME_LIMITS,
|
|
38958
39132
|
DEVICE_ORCHESTRATION_PROTOCOL: () => DEVICE_ORCHESTRATION_PROTOCOL,
|
|
38959
39133
|
DEVICE_PAIRING_INVITE_MAX_CLOCK_SKEW_MS: () => DEVICE_PAIRING_INVITE_MAX_CLOCK_SKEW_MS,
|
|
38960
39134
|
DEVICE_PAIRING_INVITE_PROTOCOL: () => DEVICE_PAIRING_INVITE_PROTOCOL,
|
|
@@ -48296,14 +48470,28 @@ var MemoryDeviceCloudTokenStorage = class {
|
|
|
48296
48470
|
loadConnectionGrant(input) {
|
|
48297
48471
|
return this.grants.get(connectionGrantCacheKey(input));
|
|
48298
48472
|
}
|
|
48299
|
-
saveConnectionGrant(input, grant) {
|
|
48300
|
-
|
|
48473
|
+
saveConnectionGrant(input, grant, fence) {
|
|
48474
|
+
const write = () => {
|
|
48475
|
+
this.grants.set(connectionGrantCacheKey(input), grant);
|
|
48476
|
+
};
|
|
48477
|
+
if (!fence) {
|
|
48478
|
+
write();
|
|
48479
|
+
return;
|
|
48480
|
+
}
|
|
48481
|
+
if (!fence.commitSynchronous(write)) fence.throwIfStale();
|
|
48301
48482
|
}
|
|
48302
48483
|
loadRelayReservation(peerId) {
|
|
48303
48484
|
return this.relayReservations.get(peerId);
|
|
48304
48485
|
}
|
|
48305
|
-
saveRelayReservation(peerId, token) {
|
|
48306
|
-
|
|
48486
|
+
saveRelayReservation(peerId, token, fence) {
|
|
48487
|
+
const write = () => {
|
|
48488
|
+
this.relayReservations.set(peerId, token);
|
|
48489
|
+
};
|
|
48490
|
+
if (!fence) {
|
|
48491
|
+
write();
|
|
48492
|
+
return;
|
|
48493
|
+
}
|
|
48494
|
+
if (!fence.commitSynchronous(write)) fence.throwIfStale();
|
|
48307
48495
|
}
|
|
48308
48496
|
clear() {
|
|
48309
48497
|
this.grants.clear();
|
|
@@ -48418,13 +48606,16 @@ var CloudDeviceFetchClient = class {
|
|
|
48418
48606
|
}
|
|
48419
48607
|
return { issuer: value.issuer, publicKeyMultibase: value.publicKeyMultibase };
|
|
48420
48608
|
}
|
|
48421
|
-
async createConnectionGrant(input, signal) {
|
|
48609
|
+
async createConnectionGrant(input, signal, fence) {
|
|
48610
|
+
assertFenceSignal(signal, fence);
|
|
48611
|
+
fence?.throwIfStale();
|
|
48422
48612
|
if (!isCanonicalConnectionGrantRequest(input)) {
|
|
48423
48613
|
throw new TypeError("invalid_connection_grant_scope");
|
|
48424
48614
|
}
|
|
48425
48615
|
const cacheInput = normalizedConnectionGrantRequest(input);
|
|
48426
48616
|
const cached = await this.loadCachedGrant(cacheInput);
|
|
48427
48617
|
throwIfAborted(signal);
|
|
48618
|
+
fence?.throwIfStale();
|
|
48428
48619
|
if (isUsableConnectionGrant(cached, cacheInput, this.now() + this.tokenSafetyMarginMs)) {
|
|
48429
48620
|
return cached;
|
|
48430
48621
|
}
|
|
@@ -48433,12 +48624,17 @@ var CloudDeviceFetchClient = class {
|
|
|
48433
48624
|
body: JSON.stringify(input)
|
|
48434
48625
|
}, signal);
|
|
48435
48626
|
if (!isUsableConnectionGrant(value, cacheInput, this.now())) throw invalidShape();
|
|
48436
|
-
|
|
48627
|
+
fence?.throwIfStale();
|
|
48628
|
+
await this.saveCachedGrant(cacheInput, value, fence);
|
|
48629
|
+
fence?.throwIfStale();
|
|
48437
48630
|
return value;
|
|
48438
48631
|
}
|
|
48439
|
-
async createRelayReservation(input, signal) {
|
|
48632
|
+
async createRelayReservation(input, signal, fence) {
|
|
48633
|
+
assertFenceSignal(signal, fence);
|
|
48634
|
+
fence?.throwIfStale();
|
|
48440
48635
|
const cached = await this.loadCachedRelayReservation(input.peerId);
|
|
48441
48636
|
throwIfAborted(signal);
|
|
48637
|
+
fence?.throwIfStale();
|
|
48442
48638
|
if (isUsableRelayReservation(cached, input.peerId, this.now() + this.tokenSafetyMarginMs)) {
|
|
48443
48639
|
return cached;
|
|
48444
48640
|
}
|
|
@@ -48447,7 +48643,9 @@ var CloudDeviceFetchClient = class {
|
|
|
48447
48643
|
body: JSON.stringify(input)
|
|
48448
48644
|
}, signal);
|
|
48449
48645
|
if (!isUsableRelayReservation(value, input.peerId, this.now())) throw invalidShape();
|
|
48450
|
-
|
|
48646
|
+
fence?.throwIfStale();
|
|
48647
|
+
await this.saveCachedRelayReservation(input.peerId, value, fence);
|
|
48648
|
+
fence?.throwIfStale();
|
|
48451
48649
|
return value;
|
|
48452
48650
|
}
|
|
48453
48651
|
async heartbeat(input, signal) {
|
|
@@ -48521,10 +48719,11 @@ var CloudDeviceFetchClient = class {
|
|
|
48521
48719
|
return void 0;
|
|
48522
48720
|
}
|
|
48523
48721
|
}
|
|
48524
|
-
async saveCachedGrant(input, grant) {
|
|
48722
|
+
async saveCachedGrant(input, grant, fence) {
|
|
48525
48723
|
try {
|
|
48526
|
-
await this.tokenStorage.saveConnectionGrant(input, grant);
|
|
48724
|
+
await this.tokenStorage.saveConnectionGrant(input, grant, fence);
|
|
48527
48725
|
} catch (error2) {
|
|
48726
|
+
if (fence && !fence.isCurrent()) fence.throwIfStale();
|
|
48528
48727
|
this.options.onTokenStorageError?.("save", error2);
|
|
48529
48728
|
}
|
|
48530
48729
|
}
|
|
@@ -48536,14 +48735,20 @@ var CloudDeviceFetchClient = class {
|
|
|
48536
48735
|
return void 0;
|
|
48537
48736
|
}
|
|
48538
48737
|
}
|
|
48539
|
-
async saveCachedRelayReservation(peerId, token) {
|
|
48738
|
+
async saveCachedRelayReservation(peerId, token, fence) {
|
|
48540
48739
|
try {
|
|
48541
|
-
await this.tokenStorage.saveRelayReservation(peerId, token);
|
|
48740
|
+
await this.tokenStorage.saveRelayReservation(peerId, token, fence);
|
|
48542
48741
|
} catch (error2) {
|
|
48742
|
+
if (fence && !fence.isCurrent()) fence.throwIfStale();
|
|
48543
48743
|
this.options.onTokenStorageError?.("save", error2);
|
|
48544
48744
|
}
|
|
48545
48745
|
}
|
|
48546
48746
|
};
|
|
48747
|
+
function assertFenceSignal(signal, fence) {
|
|
48748
|
+
if (fence && signal && fence.signal !== signal) {
|
|
48749
|
+
throw new TypeError("device_cloud_generation_signal_mismatch");
|
|
48750
|
+
}
|
|
48751
|
+
}
|
|
48547
48752
|
function normalizeCloudDeviceBaseUrl(value) {
|
|
48548
48753
|
const baseUrl = value.trim();
|
|
48549
48754
|
if (!baseUrl || baseUrl.length > CLOUD_URL_MAX_CHARACTERS) throw new Error("invalid_cloud_url");
|
|
@@ -48970,16 +49175,16 @@ var DeviceCloudConnectionCoordinator = class {
|
|
|
48970
49175
|
try {
|
|
48971
49176
|
if (this.snapshotValue.components.authorizer !== "ready") {
|
|
48972
49177
|
activeComponent = "authorizer";
|
|
48973
|
-
await this.runStep(activeComponent, generation, signal, () => this.options.adapter.ensureAuthorizer(configuration, signal));
|
|
49178
|
+
await this.runStep(activeComponent, generation, signal, (fence) => this.options.adapter.ensureAuthorizer(configuration, signal, fence));
|
|
48974
49179
|
}
|
|
48975
49180
|
if (this.snapshotValue.components.registration !== "ready") {
|
|
48976
49181
|
activeComponent = "registration";
|
|
48977
|
-
await this.runStep(activeComponent, generation, signal, () => this.options.adapter.registerDevice(configuration, signal));
|
|
49182
|
+
await this.runStep(activeComponent, generation, signal, (fence) => this.options.adapter.registerDevice(configuration, signal, fence));
|
|
48978
49183
|
}
|
|
48979
49184
|
let relayFailure;
|
|
48980
49185
|
try {
|
|
48981
49186
|
activeComponent = "relay";
|
|
48982
|
-
await this.runStep(activeComponent, generation, signal, () => this.options.adapter.ensureRelay(configuration, signal));
|
|
49187
|
+
await this.runStep(activeComponent, generation, signal, (fence) => this.options.adapter.ensureRelay(configuration, signal, fence));
|
|
48983
49188
|
} catch (error2) {
|
|
48984
49189
|
const classification = this.classify(error2);
|
|
48985
49190
|
if (classification === "registration-invalid") throw error2;
|
|
@@ -48987,11 +49192,11 @@ var DeviceCloudConnectionCoordinator = class {
|
|
|
48987
49192
|
this.warn("Device Cloud relay maintenance failed", relayFailure);
|
|
48988
49193
|
}
|
|
48989
49194
|
activeComponent = "heartbeat";
|
|
48990
|
-
await this.runStep(activeComponent, generation, signal, () => this.options.adapter.heartbeat(configuration, signal));
|
|
49195
|
+
await this.runStep(activeComponent, generation, signal, (fence) => this.options.adapter.heartbeat(configuration, signal, fence));
|
|
48991
49196
|
let directoryFailure;
|
|
48992
49197
|
try {
|
|
48993
49198
|
activeComponent = "directory";
|
|
48994
|
-
await this.runStep(activeComponent, generation, signal, () => this.options.adapter.syncDirectory(configuration, signal));
|
|
49199
|
+
await this.runStep(activeComponent, generation, signal, (fence) => this.options.adapter.syncDirectory(configuration, signal, fence));
|
|
48995
49200
|
} catch (error2) {
|
|
48996
49201
|
const classification = this.classify(error2);
|
|
48997
49202
|
if (classification === "registration-invalid") throw error2;
|
|
@@ -49031,9 +49236,9 @@ var DeviceCloudConnectionCoordinator = class {
|
|
|
49031
49236
|
if (!this.isCurrent(generation, signal)) return;
|
|
49032
49237
|
await this.setComponent(component, "pending", generation);
|
|
49033
49238
|
try {
|
|
49034
|
-
const result = await operation();
|
|
49035
|
-
if (!this.isCurrent(generation, signal)) return;
|
|
49036
49239
|
const fence = this.createCommitFence(generation, signal);
|
|
49240
|
+
const result = await operation(fence);
|
|
49241
|
+
if (!this.isCurrent(generation, signal)) return;
|
|
49037
49242
|
await result?.commit?.(fence);
|
|
49038
49243
|
if (!this.isCurrent(generation, signal)) return;
|
|
49039
49244
|
await this.setComponent(component, "ready", generation);
|
|
@@ -49737,11 +49942,18 @@ function createJsonFrameReader(source, options) {
|
|
|
49737
49942
|
|
|
49738
49943
|
// src/device-network/deviceOrchestrationTransport.ts
|
|
49739
49944
|
var DEVICE_ORCHESTRATION_PROTOCOL = "/memeloop/orchestration/2.0.0";
|
|
49740
|
-
var
|
|
49741
|
-
|
|
49742
|
-
|
|
49743
|
-
|
|
49744
|
-
|
|
49945
|
+
var DEVICE_ORCHESTRATION_FRAME_LIMITS = Object.freeze({
|
|
49946
|
+
maxPayloadBytes: 1024 * 1024,
|
|
49947
|
+
requestIdleTimeoutMs: 1e4,
|
|
49948
|
+
requestTotalTimeoutMs: 3e4,
|
|
49949
|
+
watchIdleTimeoutMs: 9e4,
|
|
49950
|
+
watchBookmarkIntervalMs: 3e4
|
|
49951
|
+
});
|
|
49952
|
+
var DEFAULT_MAX_FRAME_BYTES = DEVICE_ORCHESTRATION_FRAME_LIMITS.maxPayloadBytes;
|
|
49953
|
+
var REQUEST_IDLE_TIMEOUT_MS = DEVICE_ORCHESTRATION_FRAME_LIMITS.requestIdleTimeoutMs;
|
|
49954
|
+
var REQUEST_TOTAL_TIMEOUT_MS = DEVICE_ORCHESTRATION_FRAME_LIMITS.requestTotalTimeoutMs;
|
|
49955
|
+
var WATCH_IDLE_TIMEOUT_MS = DEVICE_ORCHESTRATION_FRAME_LIMITS.watchIdleTimeoutMs;
|
|
49956
|
+
var WATCH_BOOKMARK_INTERVAL_MS = DEVICE_ORCHESTRATION_FRAME_LIMITS.watchBookmarkIntervalMs;
|
|
49745
49957
|
function orchestrationTransportError(code, message) {
|
|
49746
49958
|
return new OrchestrationError({
|
|
49747
49959
|
code,
|
|
@@ -50022,6 +50234,13 @@ function createDeviceOrchestrationStreamHandler(options) {
|
|
|
50022
50234
|
assertFrameLimit(maxFrameBytes);
|
|
50023
50235
|
return async ({ remotePeerId, stream, authorize }) => {
|
|
50024
50236
|
const abort = new AbortController();
|
|
50237
|
+
const abortFromStream = () => {
|
|
50238
|
+
if (!abort.signal.aborted) {
|
|
50239
|
+
abort.abort(stream.signal?.reason ?? new Error("device orchestration stream closed"));
|
|
50240
|
+
}
|
|
50241
|
+
};
|
|
50242
|
+
stream.signal?.addEventListener("abort", abortFromStream, { once: true });
|
|
50243
|
+
if (stream.signal?.aborted) abortFromStream();
|
|
50025
50244
|
try {
|
|
50026
50245
|
const envelope = await readSingleRequest(stream, maxFrameBytes);
|
|
50027
50246
|
if (authorize && !await authorize(envelope.grant)) {
|
|
@@ -50045,7 +50264,8 @@ function createDeviceOrchestrationStreamHandler(options) {
|
|
|
50045
50264
|
await stream.sink(encodeJsonFrames([response], maxFrameBytes));
|
|
50046
50265
|
}
|
|
50047
50266
|
} finally {
|
|
50048
|
-
|
|
50267
|
+
stream.signal?.removeEventListener("abort", abortFromStream);
|
|
50268
|
+
if (!abort.signal.aborted) abort.abort();
|
|
50049
50269
|
await stream.close().catch(() => void 0);
|
|
50050
50270
|
}
|
|
50051
50271
|
};
|
|
@@ -50777,6 +50997,23 @@ function sameTrustedDevice(left, right) {
|
|
|
50777
50997
|
|
|
50778
50998
|
// src/device-network/standardDeviceCloudConnectionAdapter.ts
|
|
50779
50999
|
var DEFAULT_RELAY_TOKEN_SAFETY_MARGIN_MS = 2 * 6e4;
|
|
51000
|
+
var REGISTRATION_INVALID_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
51001
|
+
"device_not_found",
|
|
51002
|
+
"device_registration_invalid",
|
|
51003
|
+
"device_registration_not_found",
|
|
51004
|
+
"invalid_device_registration",
|
|
51005
|
+
"registration_invalid"
|
|
51006
|
+
]);
|
|
51007
|
+
var DeviceCloudRegistrationInvalidError = class extends Error {
|
|
51008
|
+
code = "DEVICE_CLOUD_REGISTRATION_INVALID";
|
|
51009
|
+
constructor(cause) {
|
|
51010
|
+
super(
|
|
51011
|
+
"cloud device heartbeat rejected",
|
|
51012
|
+
cause === void 0 ? void 0 : { cause }
|
|
51013
|
+
);
|
|
51014
|
+
this.name = "DeviceCloudRegistrationInvalidError";
|
|
51015
|
+
}
|
|
51016
|
+
};
|
|
50780
51017
|
var StandardDeviceCloudConnectionAdapter = class {
|
|
50781
51018
|
constructor(options) {
|
|
50782
51019
|
this.options = options;
|
|
@@ -50796,9 +51033,9 @@ var StandardDeviceCloudConnectionAdapter = class {
|
|
|
50796
51033
|
}
|
|
50797
51034
|
relayRequiredForOnline(_configuration) {
|
|
50798
51035
|
const addresses = this.options.network.getMultiaddrs();
|
|
50799
|
-
return this.options.relayRequiredForOnline?.(addresses) ?? !hasValidDirectCloudDeviceAddress(addresses);
|
|
51036
|
+
return this.options.relayRequiredForOnline?.(addresses) ?? !hasValidDirectCloudDeviceAddress(addresses, this.options.identity.peerId);
|
|
50800
51037
|
}
|
|
50801
|
-
async ensureAuthorizer(client, signal) {
|
|
51038
|
+
async ensureAuthorizer(client, signal, _fence) {
|
|
50802
51039
|
const publicKey = await client.getConnectionGrantPublicKey(signal);
|
|
50803
51040
|
return {
|
|
50804
51041
|
commit: async (fence) => {
|
|
@@ -50808,7 +51045,7 @@ var StandardDeviceCloudConnectionAdapter = class {
|
|
|
50808
51045
|
}
|
|
50809
51046
|
};
|
|
50810
51047
|
}
|
|
50811
|
-
async registerDevice(client, signal) {
|
|
51048
|
+
async registerDevice(client, signal, _fence) {
|
|
50812
51049
|
const nonce = await client.createBindingNonce(signal);
|
|
50813
51050
|
throwIfAborted3(signal);
|
|
50814
51051
|
const signature = await this.options.signDeviceBinding({
|
|
@@ -50836,27 +51073,28 @@ var StandardDeviceCloudConnectionAdapter = class {
|
|
|
50836
51073
|
}
|
|
50837
51074
|
return void 0;
|
|
50838
51075
|
}
|
|
50839
|
-
async ensureRelay(client, signal) {
|
|
51076
|
+
async ensureRelay(client, signal, fence) {
|
|
50840
51077
|
if (this.relayReservationClient === client && this.relayReservation && this.relayReservation.expiresAt > this.now() + this.relayTokenSafetyMarginMs) {
|
|
50841
51078
|
return void 0;
|
|
50842
51079
|
}
|
|
50843
51080
|
const relayReservation = await client.createRelayReservation(
|
|
50844
51081
|
{ peerId: this.options.identity.peerId },
|
|
50845
|
-
signal
|
|
51082
|
+
signal,
|
|
51083
|
+
fence
|
|
50846
51084
|
);
|
|
50847
51085
|
return {
|
|
50848
|
-
commit: async (
|
|
50849
|
-
|
|
50850
|
-
await this.options.network.configureRelayReservation(relayReservation, signal,
|
|
50851
|
-
|
|
50852
|
-
|
|
51086
|
+
commit: async (fence2) => {
|
|
51087
|
+
fence2.throwIfStale();
|
|
51088
|
+
await this.options.network.configureRelayReservation(relayReservation, signal, fence2);
|
|
51089
|
+
fence2.throwIfStale();
|
|
51090
|
+
fence2.commitSynchronous(() => {
|
|
50853
51091
|
this.relayReservation = relayReservation;
|
|
50854
51092
|
this.relayReservationClient = client;
|
|
50855
51093
|
});
|
|
50856
51094
|
}
|
|
50857
51095
|
};
|
|
50858
51096
|
}
|
|
50859
|
-
async heartbeat(client, signal) {
|
|
51097
|
+
async heartbeat(client, signal, _fence) {
|
|
50860
51098
|
throwIfAborted3(signal);
|
|
50861
51099
|
const capabilities = await this.options.capabilities();
|
|
50862
51100
|
throwIfAborted3(signal);
|
|
@@ -50874,8 +51112,16 @@ var StandardDeviceCloudConnectionAdapter = class {
|
|
|
50874
51112
|
if (!boundedProofSignature(heartbeat.signature)) {
|
|
50875
51113
|
throw new Error("invalid device heartbeat signature");
|
|
50876
51114
|
}
|
|
50877
|
-
|
|
50878
|
-
|
|
51115
|
+
let result;
|
|
51116
|
+
try {
|
|
51117
|
+
result = await client.heartbeat(heartbeat, signal);
|
|
51118
|
+
} catch (error2) {
|
|
51119
|
+
if (isHeartbeatRegistrationInvalid(error2)) {
|
|
51120
|
+
throw new DeviceCloudRegistrationInvalidError(error2);
|
|
51121
|
+
}
|
|
51122
|
+
throw error2;
|
|
51123
|
+
}
|
|
51124
|
+
if (!result.ok) throw new DeviceCloudRegistrationInvalidError();
|
|
50879
51125
|
return void 0;
|
|
50880
51126
|
}
|
|
50881
51127
|
async dispose(client, signal) {
|
|
@@ -50911,7 +51157,7 @@ var StandardDeviceCloudConnectionAdapter = class {
|
|
|
50911
51157
|
throw new AggregateError(errors, "device cloud generation cleanup failed");
|
|
50912
51158
|
}
|
|
50913
51159
|
}
|
|
50914
|
-
async syncDirectory(client, signal) {
|
|
51160
|
+
async syncDirectory(client, signal, _fence) {
|
|
50915
51161
|
const cloudDevices = await client.listDevices(signal);
|
|
50916
51162
|
return {
|
|
50917
51163
|
commit: async (fence) => {
|
|
@@ -50939,6 +51185,7 @@ var StandardDeviceCloudConnectionAdapter = class {
|
|
|
50939
51185
|
return this.options.syncDevice(client, peerId, signal);
|
|
50940
51186
|
}
|
|
50941
51187
|
classifyError(error2) {
|
|
51188
|
+
if (error2 instanceof DeviceCloudRegistrationInvalidError) return "registration-invalid";
|
|
50942
51189
|
if (error2 instanceof CloudDeviceFetchError) {
|
|
50943
51190
|
return error2.code === "cloud_request_failed" ? "offline" : "error";
|
|
50944
51191
|
}
|
|
@@ -50949,6 +51196,35 @@ var StandardDeviceCloudConnectionAdapter = class {
|
|
|
50949
51196
|
return active.length > 0 ? [...active] : [...this.relayReservation?.relayMultiaddrs ?? []];
|
|
50950
51197
|
}
|
|
50951
51198
|
};
|
|
51199
|
+
function isHeartbeatRegistrationInvalid(error2) {
|
|
51200
|
+
if (!(error2 instanceof CloudDeviceFetchError) || error2.code !== "cloud_http_error") {
|
|
51201
|
+
return false;
|
|
51202
|
+
}
|
|
51203
|
+
if (error2.status === 404) return true;
|
|
51204
|
+
return error2.status === 401 && hasExplicitRegistrationInvalidCode(error2.responseBody);
|
|
51205
|
+
}
|
|
51206
|
+
function hasExplicitRegistrationInvalidCode(responseBody) {
|
|
51207
|
+
const body = responseBody?.trim();
|
|
51208
|
+
if (!body) return false;
|
|
51209
|
+
let value;
|
|
51210
|
+
try {
|
|
51211
|
+
value = JSON.parse(body);
|
|
51212
|
+
} catch {
|
|
51213
|
+
return REGISTRATION_INVALID_ERROR_CODES.has(normalizeErrorCode(body));
|
|
51214
|
+
}
|
|
51215
|
+
return registrationInvalidCodeFromValue(value);
|
|
51216
|
+
}
|
|
51217
|
+
function registrationInvalidCodeFromValue(value) {
|
|
51218
|
+
if (typeof value === "string") {
|
|
51219
|
+
return REGISTRATION_INVALID_ERROR_CODES.has(normalizeErrorCode(value));
|
|
51220
|
+
}
|
|
51221
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
|
|
51222
|
+
const record2 = value;
|
|
51223
|
+
return registrationInvalidCodeFromValue(record2.code) || registrationInvalidCodeFromValue(record2.errorCode) || registrationInvalidCodeFromValue(record2.error);
|
|
51224
|
+
}
|
|
51225
|
+
function normalizeErrorCode(value) {
|
|
51226
|
+
return value.trim().toLowerCase().replaceAll("-", "_");
|
|
51227
|
+
}
|
|
50952
51228
|
function toPublicDeviceIdentity(identity) {
|
|
50953
51229
|
return {
|
|
50954
51230
|
peerId: identity.peerId,
|
|
@@ -50958,21 +51234,42 @@ function toPublicDeviceIdentity(identity) {
|
|
|
50958
51234
|
platform: identity.platform
|
|
50959
51235
|
};
|
|
50960
51236
|
}
|
|
50961
|
-
function hasValidDirectCloudDeviceAddress(addresses) {
|
|
50962
|
-
return addresses.some((address) =>
|
|
50963
|
-
|
|
50964
|
-
|
|
50965
|
-
|
|
50966
|
-
|
|
50967
|
-
|
|
50968
|
-
|
|
50969
|
-
|
|
50970
|
-
|
|
50971
|
-
|
|
50972
|
-
|
|
50973
|
-
|
|
50974
|
-
|
|
50975
|
-
|
|
51237
|
+
function hasValidDirectCloudDeviceAddress(addresses, expectedPeerId) {
|
|
51238
|
+
return addresses.some((address) => isDialableDirectMultiaddr(address, expectedPeerId));
|
|
51239
|
+
}
|
|
51240
|
+
function isDialableDirectMultiaddr(address, expectedPeerId) {
|
|
51241
|
+
if (address.length === 0 || address.length > 4096 || address !== address.trim() || !address.startsWith("/") || address.endsWith("/") || address.includes("//") || address.includes("/p2p-circuit")) return false;
|
|
51242
|
+
const parts = address.split("/").slice(1);
|
|
51243
|
+
if (parts.some((part) => part.length === 0 || containsAsciiControlOrSpace4(part))) return false;
|
|
51244
|
+
const hostProtocol = parts[0];
|
|
51245
|
+
const host = parts[1]?.toLowerCase();
|
|
51246
|
+
if (host === void 0 || hostProtocol !== "ip4" && hostProtocol !== "ip6" && hostProtocol !== "dns" && hostProtocol !== "dns4" && hostProtocol !== "dns6") return false;
|
|
51247
|
+
const publicHost = hostProtocol === "ip4" ? isPublicIpv4(host) : hostProtocol === "ip6" ? isPublicIpv6(host) : isPlausiblePublicDnsHost(host);
|
|
51248
|
+
if (!publicHost) return false;
|
|
51249
|
+
const transport = parts[2];
|
|
51250
|
+
const port = parsePort(parts[3]);
|
|
51251
|
+
if (transport !== "tcp" || port === void 0) return false;
|
|
51252
|
+
let index = 4;
|
|
51253
|
+
if (parts[index] === "ws" || parts[index] === "wss") index += 1;
|
|
51254
|
+
if (parts[index] === "p2p") {
|
|
51255
|
+
const peerId = parts[index + 1];
|
|
51256
|
+
if (!peerId || containsAsciiControlOrSpace4(peerId)) return false;
|
|
51257
|
+
if (expectedPeerId !== void 0 && peerId !== expectedPeerId) return false;
|
|
51258
|
+
index += 2;
|
|
51259
|
+
}
|
|
51260
|
+
return index === parts.length;
|
|
51261
|
+
}
|
|
51262
|
+
function parsePort(value) {
|
|
51263
|
+
if (value === void 0 || !/^\d{1,5}$/u.test(value)) return void 0;
|
|
51264
|
+
const port = Number(value);
|
|
51265
|
+
return port >= 1 && port <= 65535 ? port : void 0;
|
|
51266
|
+
}
|
|
51267
|
+
function containsAsciiControlOrSpace4(value) {
|
|
51268
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
51269
|
+
const code = value.charCodeAt(index);
|
|
51270
|
+
if (code <= 32 || code === 127) return true;
|
|
51271
|
+
}
|
|
51272
|
+
return false;
|
|
50976
51273
|
}
|
|
50977
51274
|
function isPublicIpv4(host) {
|
|
50978
51275
|
const octets = host.split(".").map((part) => Number(part));
|
|
@@ -51012,7 +51309,17 @@ function isPlausiblePublicDnsHost(host) {
|
|
|
51012
51309
|
const normalized = host.endsWith(".") ? host.slice(0, -1) : host;
|
|
51013
51310
|
if (isIpv4Syntax(normalized)) return isPublicIpv4(normalized);
|
|
51014
51311
|
if (normalized.includes(":")) return isPublicIpv6(normalized);
|
|
51015
|
-
|
|
51312
|
+
const specialUseSuffixes = [
|
|
51313
|
+
"example",
|
|
51314
|
+
"home.arpa",
|
|
51315
|
+
"internal",
|
|
51316
|
+
"invalid",
|
|
51317
|
+
"local",
|
|
51318
|
+
"localhost",
|
|
51319
|
+
"onion",
|
|
51320
|
+
"test"
|
|
51321
|
+
];
|
|
51322
|
+
if (specialUseSuffixes.some((suffix) => normalized === suffix || normalized.endsWith(`.${suffix}`))) return false;
|
|
51016
51323
|
const labels = normalized.split(".");
|
|
51017
51324
|
return labels.length > 1 && labels.every(
|
|
51018
51325
|
(label) => label.length > 0 && label.length <= 63 && /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/u.test(label)
|
|
@@ -51840,7 +52147,7 @@ function readDataIdentifier(value, property, field) {
|
|
|
51840
52147
|
}
|
|
51841
52148
|
|
|
51842
52149
|
// src/plugin/loader.ts
|
|
51843
|
-
var MEMELOOP_PLUGIN_API_VERSION = "0.2.
|
|
52150
|
+
var MEMELOOP_PLUGIN_API_VERSION = "0.2.9";
|
|
51844
52151
|
var dangerousManifestKeys = /* @__PURE__ */ new Set(["__proto__", "prototype", "constructor"]);
|
|
51845
52152
|
var manifestKeys = /* @__PURE__ */ new Set(["name", "version", "description", "exports", "author", "minMemeloopVersion"]);
|
|
51846
52153
|
var exportKeys = /* @__PURE__ */ new Set([
|
|
@@ -53359,6 +53666,7 @@ var ToolDefinitionRegistry = class {
|
|
|
53359
53666
|
DEVICE_HEARTBEAT_DEFAULT_MAX_CLOCK_SKEW_MS,
|
|
53360
53667
|
DEVICE_HEARTBEAT_LIMITS,
|
|
53361
53668
|
DEVICE_HEARTBEAT_SIGNATURE_DOMAIN,
|
|
53669
|
+
DEVICE_ORCHESTRATION_FRAME_LIMITS,
|
|
53362
53670
|
DEVICE_ORCHESTRATION_PROTOCOL,
|
|
53363
53671
|
DEVICE_PAIRING_INVITE_MAX_CLOCK_SKEW_MS,
|
|
53364
53672
|
DEVICE_PAIRING_INVITE_PROTOCOL,
|