@camstack/system 1.2.381 → 1.2.383
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/addon-runner.js +4 -2
- package/dist/addon-runner.mjs +4 -2
- package/dist/addon-utils.js +1 -1
- package/dist/addon-utils.mjs +1 -1
- package/dist/auth/admin-token-manager.d.ts +75 -0
- package/dist/auth/api-key-manager.d.ts +1 -14
- package/dist/auth/auth-audit-log.d.ts +59 -0
- package/dist/auth/auth-manager.d.ts +28 -16
- package/dist/auth/cidr.d.ts +22 -0
- package/dist/auth/login-session-manager.d.ts +158 -0
- package/dist/auth/user-management-internal.d.ts +69 -0
- package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +1 -1
- package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +1 -1
- package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +1 -1
- package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +1 -1
- package/dist/builtins/alerts/alerts.addon.js +1 -1
- package/dist/builtins/alerts/alerts.addon.mjs +1 -1
- package/dist/builtins/autotrack/index.js +1 -1
- package/dist/builtins/autotrack/index.mjs +1 -1
- package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.d.ts +14 -0
- package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +291 -3631
- package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +284 -3617
- package/dist/builtins/backup-orchestrator/encryption-key-store.d.ts +36 -0
- package/dist/builtins/backup-orchestrator/manifest-store.d.ts +19 -1
- package/dist/builtins/backup-orchestrator/restore-archive.d.ts +24 -0
- package/dist/builtins/calendars/calendars.addon.js +1 -1
- package/dist/builtins/calendars/calendars.addon.mjs +1 -1
- package/dist/builtins/camera-grid/index.js +1 -1
- package/dist/builtins/camera-grid/index.mjs +1 -1
- package/dist/builtins/composer/composer.addon.js +2 -2
- package/dist/builtins/composer/composer.addon.mjs +2 -2
- package/dist/builtins/console-logging/index.js +1 -1
- package/dist/builtins/console-logging/index.mjs +1 -1
- package/dist/builtins/core-blocks/core-blocks.addon.js +2 -2
- package/dist/builtins/core-blocks/core-blocks.addon.mjs +2 -2
- package/dist/builtins/device-manager/device-manager.addon.js +2 -2
- package/dist/builtins/device-manager/device-manager.addon.mjs +2 -2
- package/dist/builtins/hub-forwarder/index.js +1 -1
- package/dist/builtins/hub-forwarder/index.mjs +1 -1
- package/dist/builtins/keychain/index.js +1110 -735
- package/dist/builtins/keychain/index.mjs +1109 -734
- package/dist/builtins/keychain/keychain-reset-trigger.d.ts +25 -0
- package/dist/builtins/keychain/keychain-service.d.ts +36 -3
- package/dist/builtins/keychain/keychain-store.d.ts +24 -1
- package/dist/builtins/keychain/system-held-secrets.d.ts +2 -1
- package/dist/builtins/liveness-monitor/liveness-monitor.addon.js +1 -1
- package/dist/builtins/liveness-monitor/liveness-monitor.addon.mjs +1 -1
- package/dist/builtins/local-auth/local-auth.addon.d.ts +16 -0
- package/dist/builtins/local-auth/local-auth.addon.js +1028 -294
- package/dist/builtins/local-auth/local-auth.addon.mjs +1035 -299
- package/dist/builtins/local-network/local-network.addon.js +3 -3
- package/dist/builtins/local-network/local-network.addon.mjs +3 -3
- package/dist/builtins/loki-logging/index.js +97 -29
- package/dist/builtins/loki-logging/index.mjs +97 -29
- package/dist/builtins/loki-logging/loki-logging.addon.d.ts +7 -23
- package/dist/builtins/loki-logging/retire-enabled-flag.d.ts +23 -0
- package/dist/builtins/native-metrics/native-metrics.addon.js +1 -1
- package/dist/builtins/native-metrics/native-metrics.addon.mjs +1 -1
- package/dist/builtins/platform-probe/index.js +1 -1
- package/dist/builtins/platform-probe/index.mjs +1 -1
- package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
- package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
- package/dist/builtins/snapshot/index.js +1 -1
- package/dist/builtins/snapshot/index.mjs +1 -1
- package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
- package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
- package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +0 -0
- package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +0 -0
- package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +2 -2
- package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +2 -2
- package/dist/builtins/system-backup/backup-encryption.d.ts +57 -0
- package/dist/builtins/system-backup/system-backup.service.d.ts +16 -1
- package/dist/builtins/system-config/cluster-secret-keeper.d.ts +7 -0
- package/dist/builtins/system-config/index.d.ts +1 -0
- package/dist/builtins/system-config/index.js +2 -0
- package/dist/builtins/system-config/index.mjs +2 -2
- package/dist/builtins/system-config/mesh-actions.d.ts +19 -0
- package/dist/builtins/system-config/system-config.addon.d.ts +47 -4
- package/dist/builtins/system-config/system-config.addon.js +87858 -43
- package/dist/builtins/system-config/system-config.addon.mjs +87813 -39
- package/dist/builtins/winston-logging/index.js +1 -1
- package/dist/builtins/winston-logging/index.mjs +1 -1
- package/dist/{child-cap-dispatch-hf-Gce_4.js → child-cap-dispatch-BD8fhJ1b.js} +7 -4
- package/dist/{child-cap-dispatch-BTe4eKep.mjs → child-cap-dispatch-C68N2a6Y.mjs} +11 -8
- package/dist/{composition-sources-4SmsP0O4.js → composition-sources-ChpT_Yw4.js} +1 -1
- package/dist/{composition-sources-CgOQVIwa.mjs → composition-sources-DM7Ft_tp.mjs} +1 -1
- package/dist/{cluster-secret-BUiG8DCo.js → constants-CDpry1_T.js} +82 -1
- package/dist/{cluster-secret-DgJTZ4qY.mjs → constants-e3mCa1jG.mjs} +23 -2
- package/dist/{dist-BugrtARh.js → dist-CUNGH0fs.js} +1755 -157
- package/dist/{dist-BC0GnGeg.mjs → dist-osl3bkF0.mjs} +1732 -158
- package/dist/{event-loop-stall-monitor-GgpnfAC0.mjs → event-loop-stall-monitor-BeeWMnyT.mjs} +15 -15
- package/dist/{event-loop-stall-monitor-CnQ-K6Gn.js → event-loop-stall-monitor-Dk28EF8B.js} +15 -15
- package/dist/{hub-cap-forward-CH6_VWVO.mjs → hub-cap-forward-CDiN1p98.mjs} +18 -2
- package/dist/{hub-cap-forward-COCmYaup.js → hub-cap-forward-W8oEm4ME.js} +23 -1
- package/dist/index.d.ts +16 -1
- package/dist/index.js +1023 -83525
- package/dist/index.mjs +1218 -83794
- package/dist/{jwt-signing-key-CsuVKA1W.mjs → jwt-signing-key-MQLxLfgF.mjs} +1 -1
- package/dist/{jwt-signing-key-CjoZcol8.js → jwt-signing-key-WI6z7JBo.js} +1 -1
- package/dist/kernel/index.d.ts +17 -1
- package/dist/kernel/moleculer/agent-mesh-authenticator.d.ts +41 -0
- package/dist/kernel/moleculer/broker-factory.d.ts +25 -2
- package/dist/kernel/moleculer/child-addon-call-dispatch.d.ts +1 -0
- package/dist/kernel/moleculer/hub-service.d.ts +9 -0
- package/dist/kernel/moleculer/mesh-access-middleware.d.ts +33 -0
- package/dist/kernel/moleculer/mesh-auth-mode.d.ts +64 -0
- package/dist/kernel/moleculer/mesh-authenticator.d.ts +71 -0
- package/dist/kernel/moleculer/mesh-auto-enforce.d.ts +26 -0
- package/dist/kernel/moleculer/mesh-join-client.d.ts +51 -0
- package/dist/kernel/moleculer/mesh-join-proof.d.ts +39 -0
- package/dist/kernel/moleculer/mesh-mtls/constants.d.ts +20 -0
- package/dist/kernel/moleculer/mesh-mtls/index.d.ts +9 -0
- package/dist/kernel/moleculer/mesh-mtls/mesh-ca-keeper.d.ts +55 -0
- package/dist/kernel/moleculer/mesh-mtls/mesh-ca.d.ts +45 -0
- package/dist/kernel/moleculer/mesh-mtls/mesh-crl.d.ts +16 -0
- package/dist/kernel/moleculer/mesh-mtls/mesh-enroll.d.ts +27 -0
- package/dist/kernel/moleculer/mesh-mtls/mesh-join-token.d.ts +55 -0
- package/dist/kernel/moleculer/mesh-mtls/mesh-tls-enabler.d.ts +5 -0
- package/dist/kernel/moleculer/mesh-mtls/tls-tcp-transporter.d.ts +92 -0
- package/dist/kernel/system-settings-policy.d.ts +4 -3
- package/dist/kernel/transport/hub-cap-forward.d.ts +14 -3
- package/dist/kernel/transport/index.d.ts +1 -1
- package/dist/ms-DlIWNkkk.mjs +160 -0
- package/dist/ms-t68_FaqE.js +171 -0
- package/dist/restore-archive-C5f9yX90.js +4139 -0
- package/dist/restore-archive-CZRtI1k9.mjs +4045 -0
- package/dist/{retired-settings-keys-MCVhxPkJ.mjs → retired-settings-keys-LuDd4y3M.mjs} +1 -1
- package/dist/{retired-settings-keys-DCuetwOt.js → retired-settings-keys-WLrbzdPR.js} +1 -1
- package/dist/tls-BWpwU-d5.js +1160 -0
- package/dist/tls-C2NT-Orr.mjs +949 -0
- package/dist/{tls-CCMX7MjX.js → x509.es-BhTzcFhZ.js} +65 -1108
- package/dist/{tls-CJHogwTl.mjs → x509.es-Xf3K_brW.mjs} +40 -950
- package/package.json +3 -2
|
@@ -708,7 +708,7 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
|
708
708
|
return EventCategory;
|
|
709
709
|
}({});
|
|
710
710
|
//#endregion
|
|
711
|
-
//#region ../types/dist/sleep-
|
|
711
|
+
//#region ../types/dist/sleep-r3RWkGVx.mjs
|
|
712
712
|
/**
|
|
713
713
|
* The audio chunk plane's byte format, and the ONE expansion from a coded
|
|
714
714
|
* window to float samples (D455).
|
|
@@ -820,21 +820,21 @@ function collectSecretConfigKeys(schema) {
|
|
|
820
820
|
for (const section of sectionsOf(schema)) for (const field of fieldsOf(section)) walkField(field, keys);
|
|
821
821
|
return keys;
|
|
822
822
|
}
|
|
823
|
-
function isRecord$
|
|
823
|
+
function isRecord$3(value) {
|
|
824
824
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
825
825
|
}
|
|
826
826
|
function sectionsOf(schema) {
|
|
827
|
-
if (!isRecord$
|
|
827
|
+
if (!isRecord$3(schema)) return [];
|
|
828
828
|
const sections = schema["sections"];
|
|
829
829
|
return Array.isArray(sections) ? sections : [];
|
|
830
830
|
}
|
|
831
831
|
function fieldsOf(node) {
|
|
832
|
-
if (!isRecord$
|
|
832
|
+
if (!isRecord$3(node)) return [];
|
|
833
833
|
const fields = node["fields"];
|
|
834
834
|
return Array.isArray(fields) ? fields : [];
|
|
835
835
|
}
|
|
836
836
|
function walkField(field, out) {
|
|
837
|
-
if (!isRecord$
|
|
837
|
+
if (!isRecord$3(field)) return;
|
|
838
838
|
const type = field["type"];
|
|
839
839
|
const key = field["key"];
|
|
840
840
|
if ((type === "password" || field["secret"] === true) && typeof key === "string" && key.length > 0) out.add(key);
|
|
@@ -872,7 +872,7 @@ function walkField(field, out) {
|
|
|
872
872
|
var SECRET_CLEAR = { $secretClear: true };
|
|
873
873
|
/** The `'***'` token D634 redaction writes for non-admins. */
|
|
874
874
|
var LEGACY_DEVICE_REDACTION = "***";
|
|
875
|
-
function isRecord$
|
|
875
|
+
function isRecord$2(value) {
|
|
876
876
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
877
877
|
}
|
|
878
878
|
function makeSecretMarker(ref) {
|
|
@@ -883,16 +883,16 @@ function makeSecretMarker(ref) {
|
|
|
883
883
|
}
|
|
884
884
|
/** A stored marker: an object whose `$secret` is a non-empty string. */
|
|
885
885
|
function isSecretMarker(value) {
|
|
886
|
-
if (!isRecord$
|
|
886
|
+
if (!isRecord$2(value)) return false;
|
|
887
887
|
const ref = value["$secret"];
|
|
888
888
|
return typeof ref === "string" && ref.length > 0;
|
|
889
889
|
}
|
|
890
890
|
/** Anything carrying a `$secret` key — a stored marker or a UI view of one. */
|
|
891
891
|
function isSecretMarkerLike(value) {
|
|
892
|
-
return isRecord$
|
|
892
|
+
return isRecord$2(value) && Object.prototype.hasOwnProperty.call(value, "$secret");
|
|
893
893
|
}
|
|
894
894
|
function isSecretClear(value) {
|
|
895
|
-
return isRecord$
|
|
895
|
+
return isRecord$2(value) && value["$secretClear"] === true;
|
|
896
896
|
}
|
|
897
897
|
/**
|
|
898
898
|
* The UI view of a stored secret field value: a marker becomes "set", inline
|
|
@@ -1561,7 +1561,7 @@ var KeychainWriteRefusedError = class extends Error {
|
|
|
1561
1561
|
}
|
|
1562
1562
|
};
|
|
1563
1563
|
var KEEP = { kind: "keep" };
|
|
1564
|
-
function isRecord(value) {
|
|
1564
|
+
function isRecord$1(value) {
|
|
1565
1565
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1566
1566
|
}
|
|
1567
1567
|
function defaultMintId() {
|
|
@@ -1574,7 +1574,7 @@ function collectSecretMarkerRefs(value, out = []) {
|
|
|
1574
1574
|
return out;
|
|
1575
1575
|
}
|
|
1576
1576
|
if (Array.isArray(value)) for (const item of value) collectSecretMarkerRefs(item, out);
|
|
1577
|
-
else if (isRecord(value)) for (const child of Object.values(value)) collectSecretMarkerRefs(child, out);
|
|
1577
|
+
else if (isRecord$1(value)) for (const child of Object.values(value)) collectSecretMarkerRefs(child, out);
|
|
1578
1578
|
return out;
|
|
1579
1579
|
}
|
|
1580
1580
|
async function storedValueEquals(secrets, marker, candidate) {
|
|
@@ -1629,7 +1629,7 @@ function rowId(row, idKey) {
|
|
|
1629
1629
|
return null;
|
|
1630
1630
|
}
|
|
1631
1631
|
async function writeArray(ctx, node, incoming, stored, fieldPath) {
|
|
1632
|
-
const storedRows = Array.isArray(stored) ? stored.filter(isRecord) : [];
|
|
1632
|
+
const storedRows = Array.isArray(stored) ? stored.filter(isRecord$1) : [];
|
|
1633
1633
|
if (!Array.isArray(incoming)) {
|
|
1634
1634
|
ctx.deletes.push(...collectSecretMarkerRefs(storedRows));
|
|
1635
1635
|
return {
|
|
@@ -1645,7 +1645,7 @@ async function writeArray(ctx, node, incoming, stored, fieldPath) {
|
|
|
1645
1645
|
const kept = /* @__PURE__ */ new Set();
|
|
1646
1646
|
const rows = [];
|
|
1647
1647
|
for (const row of incoming) {
|
|
1648
|
-
if (!isRecord(row)) {
|
|
1648
|
+
if (!isRecord$1(row)) {
|
|
1649
1649
|
rows.push(row);
|
|
1650
1650
|
continue;
|
|
1651
1651
|
}
|
|
@@ -1670,7 +1670,7 @@ async function writeNode(ctx, node, incoming, stored, fieldPath) {
|
|
|
1670
1670
|
case "leaf": return writeLeaf(ctx, incoming, stored, fieldPath);
|
|
1671
1671
|
case "array": return writeArray(ctx, node, incoming, stored, fieldPath);
|
|
1672
1672
|
case "object": {
|
|
1673
|
-
if (!isRecord(incoming)) {
|
|
1673
|
+
if (!isRecord$1(incoming)) {
|
|
1674
1674
|
if (incoming === void 0) return KEEP;
|
|
1675
1675
|
ctx.deletes.push(...collectSecretMarkerRefs(stored));
|
|
1676
1676
|
return {
|
|
@@ -1678,7 +1678,7 @@ async function writeNode(ctx, node, incoming, stored, fieldPath) {
|
|
|
1678
1678
|
value: incoming
|
|
1679
1679
|
};
|
|
1680
1680
|
}
|
|
1681
|
-
const storedObj = isRecord(stored) ? stored : {};
|
|
1681
|
+
const storedObj = isRecord$1(stored) ? stored : {};
|
|
1682
1682
|
return {
|
|
1683
1683
|
kind: "value",
|
|
1684
1684
|
value: await writeMembers(ctx, node.children, incoming, storedObj, (m) => `${fieldPath}.${m}`)
|
|
@@ -1737,12 +1737,19 @@ async function transformSecretWrite(input) {
|
|
|
1737
1737
|
}
|
|
1738
1738
|
};
|
|
1739
1739
|
}
|
|
1740
|
+
/** Is `ref` one of `owner`'s refs? */
|
|
1741
|
+
function isSecretRefOwnedBy(ref, owner) {
|
|
1742
|
+
return parseSecretRef(ref)?.owner === owner;
|
|
1743
|
+
}
|
|
1740
1744
|
/**
|
|
1741
1745
|
* Replace every marker in `value` with its keychain value. Returns the SAME
|
|
1742
1746
|
* reference when there is no marker (no allocation, no keychain call).
|
|
1743
1747
|
*/
|
|
1744
|
-
async function resolveSecretMarkers(value, secrets) {
|
|
1745
|
-
const
|
|
1748
|
+
async function resolveSecretMarkers(value, secrets, options = {}) {
|
|
1749
|
+
const keepMarker = options.unresolved === "keep-marker";
|
|
1750
|
+
const owner = options.ownedBy;
|
|
1751
|
+
const inScope = (ref) => owner === void 0 || isSecretRefOwnedBy(ref, owner);
|
|
1752
|
+
const refs = collectSecretMarkerRefs(value).filter(inScope);
|
|
1746
1753
|
if (refs.length === 0) return {
|
|
1747
1754
|
value,
|
|
1748
1755
|
unresolved: []
|
|
@@ -1766,10 +1773,18 @@ async function resolveSecretMarkers(value, secrets) {
|
|
|
1766
1773
|
}
|
|
1767
1774
|
const substitute = (v) => {
|
|
1768
1775
|
if (isSecretMarker(v)) {
|
|
1776
|
+
if (!inScope(v.$secret)) return {
|
|
1777
|
+
keep: true,
|
|
1778
|
+
value: v
|
|
1779
|
+
};
|
|
1769
1780
|
const answer = answers[v.$secret];
|
|
1770
|
-
|
|
1781
|
+
if (answer?.kind === "value") return {
|
|
1771
1782
|
keep: true,
|
|
1772
1783
|
value: answer.value
|
|
1784
|
+
};
|
|
1785
|
+
return keepMarker ? {
|
|
1786
|
+
keep: true,
|
|
1787
|
+
value: v
|
|
1773
1788
|
} : {
|
|
1774
1789
|
keep: false,
|
|
1775
1790
|
value: void 0
|
|
@@ -1786,7 +1801,7 @@ async function resolveSecretMarkers(value, secrets) {
|
|
|
1786
1801
|
value: items
|
|
1787
1802
|
};
|
|
1788
1803
|
}
|
|
1789
|
-
if (isRecord(v)) {
|
|
1804
|
+
if (isRecord$1(v)) {
|
|
1790
1805
|
const out = {};
|
|
1791
1806
|
for (const [k, child] of Object.entries(v)) {
|
|
1792
1807
|
const s = substitute(child);
|
|
@@ -1807,14 +1822,152 @@ async function resolveSecretMarkers(value, secrets) {
|
|
|
1807
1822
|
unresolved
|
|
1808
1823
|
};
|
|
1809
1824
|
}
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1825
|
+
/** An addon tried to replace a stored secret it could not read. Nothing was written. */
|
|
1826
|
+
var SecretOverwriteRefusedError = class extends Error {
|
|
1827
|
+
ref;
|
|
1828
|
+
reason;
|
|
1829
|
+
constructor(ref, reason) {
|
|
1830
|
+
super(`secret ${ref} was not replaced: its stored value cannot be read right now (${reason}), and an addon may not overwrite a secret it cannot see. Nothing was written.`);
|
|
1831
|
+
this.ref = ref;
|
|
1832
|
+
this.reason = reason;
|
|
1833
|
+
this.name = "SecretOverwriteRefusedError";
|
|
1834
|
+
}
|
|
1835
|
+
};
|
|
1836
|
+
function isRecord(value) {
|
|
1837
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1838
|
+
}
|
|
1839
|
+
function resolvedValue(pass, marker) {
|
|
1840
|
+
const answer = pass.answers[marker.$secret];
|
|
1841
|
+
return answer?.kind === "value" ? answer.value : null;
|
|
1842
|
+
}
|
|
1843
|
+
async function atMarker(pass, incoming, stored) {
|
|
1844
|
+
if (incoming === null) return stored;
|
|
1845
|
+
if (typeof incoming !== "string") return incoming;
|
|
1846
|
+
if (incoming === resolvedValue(pass, stored)) return stored;
|
|
1847
|
+
const intent = classifySecretWrite(incoming);
|
|
1848
|
+
if (intent.kind !== "set") return stored;
|
|
1849
|
+
const ref = stored.$secret;
|
|
1850
|
+
if (!isSecretRefOwnedBy(ref, pass.secrets.owner)) return incoming;
|
|
1851
|
+
const answer = pass.answers[ref];
|
|
1852
|
+
if ((answer === void 0 || answer.kind === "unavailable" || answer.kind === "forbidden") && !pass.operatorWrite) throw new SecretOverwriteRefusedError(ref, answer === void 0 ? "not-initialized" : answer.kind === "unavailable" ? answer.reason : answer.kind);
|
|
1853
|
+
try {
|
|
1854
|
+
await pass.secrets.put(ref, intent.value);
|
|
1855
|
+
} catch (err) {
|
|
1856
|
+
throw new KeychainWriteRefusedError(ref, err);
|
|
1857
|
+
}
|
|
1858
|
+
pass.changedRefs.push(ref);
|
|
1859
|
+
return stored;
|
|
1860
|
+
}
|
|
1861
|
+
function inArray(pass, incoming, stored) {
|
|
1862
|
+
return incoming.map((item, index) => {
|
|
1863
|
+
const twin = stored[index];
|
|
1864
|
+
if (isSecretMarker(twin)) return typeof item === "string" && item === resolvedValue(pass, twin) ? twin : item;
|
|
1865
|
+
if (isRecord(item) && isRecord(twin)) return inArrayRow(pass, item, twin);
|
|
1866
|
+
if (Array.isArray(item) && Array.isArray(twin)) return inArray(pass, item, twin);
|
|
1867
|
+
return item;
|
|
1868
|
+
});
|
|
1869
|
+
}
|
|
1870
|
+
function inArrayRow(pass, incoming, stored) {
|
|
1871
|
+
const out = { ...incoming };
|
|
1872
|
+
for (const [key, value] of Object.entries(incoming)) {
|
|
1873
|
+
const twin = stored[key];
|
|
1874
|
+
if (isSecretMarker(twin)) {
|
|
1875
|
+
if (typeof value === "string" && value === resolvedValue(pass, twin)) out[key] = twin;
|
|
1876
|
+
} else if (isRecord(value) && isRecord(twin)) out[key] = inArrayRow(pass, value, twin);
|
|
1877
|
+
else if (Array.isArray(value) && Array.isArray(twin)) out[key] = inArray(pass, value, twin);
|
|
1878
|
+
}
|
|
1879
|
+
return out;
|
|
1880
|
+
}
|
|
1881
|
+
async function walk(pass, incoming, stored) {
|
|
1882
|
+
if (isSecretMarker(stored)) return atMarker(pass, incoming, stored);
|
|
1883
|
+
if (Array.isArray(incoming) && Array.isArray(stored)) return inArray(pass, incoming, stored);
|
|
1884
|
+
if (!isRecord(incoming) || !isRecord(stored)) return incoming;
|
|
1885
|
+
const out = { ...incoming };
|
|
1886
|
+
for (const [key, value] of Object.entries(incoming)) out[key] = await walk(pass, value, stored[key]);
|
|
1887
|
+
return out;
|
|
1888
|
+
}
|
|
1889
|
+
/** Restore markers a write-back carries as values. See the file doc. */
|
|
1890
|
+
async function restoreUnchangedSecrets(input) {
|
|
1891
|
+
const refs = [];
|
|
1892
|
+
for (const key of Object.keys(input.patch)) collectSecretMarkerRefs(input.stored[key], refs);
|
|
1893
|
+
const owned = refs.filter((ref) => isSecretRefOwnedBy(ref, input.secrets.owner));
|
|
1894
|
+
if (owned.length === 0) return {
|
|
1895
|
+
patch: input.patch,
|
|
1896
|
+
changedRefs: []
|
|
1897
|
+
};
|
|
1898
|
+
const pass = {
|
|
1899
|
+
secrets: input.secrets,
|
|
1900
|
+
answers: await input.secrets.resolve([...new Set(owned)]),
|
|
1901
|
+
operatorWrite: input.operatorWrite,
|
|
1902
|
+
changedRefs: []
|
|
1903
|
+
};
|
|
1904
|
+
const out = {};
|
|
1905
|
+
for (const [key, value] of Object.entries(input.patch)) out[key] = await walk(pass, value, input.stored[key]);
|
|
1906
|
+
return {
|
|
1907
|
+
patch: out,
|
|
1908
|
+
changedRefs: pass.changedRefs
|
|
1909
|
+
};
|
|
1910
|
+
}
|
|
1911
|
+
/** Structural equality over JSON-shaped values (key order does not matter). */
|
|
1912
|
+
function isJsonEqual(a, b) {
|
|
1913
|
+
if (Object.is(a, b)) return true;
|
|
1914
|
+
if (Array.isArray(a)) return Array.isArray(b) && a.length === b.length && a.every((v, i) => isJsonEqual(v, b[i]));
|
|
1915
|
+
if (isRecord(a) && isRecord(b)) {
|
|
1916
|
+
const keys = Object.keys(a);
|
|
1917
|
+
if (keys.length !== Object.keys(b).length) return false;
|
|
1918
|
+
return keys.every((k) => Object.prototype.hasOwnProperty.call(b, k) && isJsonEqual(a[k], b[k]));
|
|
1815
1919
|
}
|
|
1816
1920
|
return false;
|
|
1817
1921
|
}
|
|
1922
|
+
/** The patch without the keys whose value already equals the stored one. */
|
|
1923
|
+
function dropUnchangedKeys(patch, stored) {
|
|
1924
|
+
const out = {};
|
|
1925
|
+
for (const [key, value] of Object.entries(patch)) {
|
|
1926
|
+
if (Object.prototype.hasOwnProperty.call(stored, key) && isJsonEqual(value, stored[key])) continue;
|
|
1927
|
+
out[key] = value;
|
|
1928
|
+
}
|
|
1929
|
+
return out;
|
|
1930
|
+
}
|
|
1931
|
+
/** The top-level keys of a blob that hold at least one of `owner`'s markers. */
|
|
1932
|
+
function markerKeysOf(blob, owner) {
|
|
1933
|
+
const keys = /* @__PURE__ */ new Set();
|
|
1934
|
+
for (const [key, value] of Object.entries(blob)) if (collectSecretMarkerRefs(value).some((ref) => isSecretRefOwnedBy(ref, owner))) keys.add(key);
|
|
1935
|
+
return keys;
|
|
1936
|
+
}
|
|
1937
|
+
function nodeFromStored(value, owner) {
|
|
1938
|
+
if (isSecretMarker(value)) return isSecretRefOwnedBy(value.$secret, owner) ? { kind: "leaf" } : null;
|
|
1939
|
+
if (!isRecord(value)) return null;
|
|
1940
|
+
const children = /* @__PURE__ */ new Map();
|
|
1941
|
+
for (const [key, child] of Object.entries(value)) {
|
|
1942
|
+
const node = nodeFromStored(child, owner);
|
|
1943
|
+
if (node !== null) children.set(key, node);
|
|
1944
|
+
}
|
|
1945
|
+
return children.size > 0 ? {
|
|
1946
|
+
kind: "object",
|
|
1947
|
+
children
|
|
1948
|
+
} : null;
|
|
1949
|
+
}
|
|
1950
|
+
/**
|
|
1951
|
+
* The declared secret tree, widened by every place the STORED blob holds a
|
|
1952
|
+
* marker under one of `keys` — so a marker the schema no longer declares (or
|
|
1953
|
+
* never declared: Wyze before b4906cec6) still gets the save path's
|
|
1954
|
+
* KEEP / SET / CLEAR semantics instead of being overwritten by whatever the
|
|
1955
|
+
* addon writes. Markers inside arrays are not added: a row has no id field to
|
|
1956
|
+
* match by unless the schema declares one. Only `owner`'s markers count: a
|
|
1957
|
+
* builtin that keeps another addon's marker (device-manager) never takes it
|
|
1958
|
+
* over.
|
|
1959
|
+
*/
|
|
1960
|
+
function widenTreeWithStoredMarkers(declared, stored, keys, owner) {
|
|
1961
|
+
const added = /* @__PURE__ */ new Map();
|
|
1962
|
+
for (const key of keys) {
|
|
1963
|
+
const at = key.indexOf("@");
|
|
1964
|
+
const base = at > 0 ? key.slice(0, at) : key;
|
|
1965
|
+
if (declared.has(base) || added.has(base)) continue;
|
|
1966
|
+
const node = nodeFromStored(stored[key], owner);
|
|
1967
|
+
if (node !== null) added.set(base, node);
|
|
1968
|
+
}
|
|
1969
|
+
return added.size === 0 ? declared : new Map([...declared, ...added]);
|
|
1970
|
+
}
|
|
1818
1971
|
/** The blob as stored now; a failed read REFUSES the write instead of guessing. */
|
|
1819
1972
|
async function readStoredAddonBlob(raw) {
|
|
1820
1973
|
const threeWay = raw.readAddonStoreResult;
|
|
@@ -1823,7 +1976,41 @@ async function readStoredAddonBlob(raw) {
|
|
|
1823
1976
|
if (result.kind === "failed") throw new Error(`settings write refused: the stored blob could not be read (${result.error})`);
|
|
1824
1977
|
return result.kind === "missing" ? {} : result.value;
|
|
1825
1978
|
}
|
|
1826
|
-
function
|
|
1979
|
+
function touchesTree(tree, patch) {
|
|
1980
|
+
if (tree.size === 0) return false;
|
|
1981
|
+
for (const key of Object.keys(patch)) {
|
|
1982
|
+
const at = key.indexOf("@");
|
|
1983
|
+
if (tree.has(at > 0 ? key.slice(0, at) : key)) return true;
|
|
1984
|
+
}
|
|
1985
|
+
return false;
|
|
1986
|
+
}
|
|
1987
|
+
/**
|
|
1988
|
+
* Which top-level keys of each store held markers when last seen. `undefined`
|
|
1989
|
+
* for a store not seen yet: its first write reads it, to be safe.
|
|
1990
|
+
*/
|
|
1991
|
+
var MarkerKeyIndex = class {
|
|
1992
|
+
owner;
|
|
1993
|
+
byStore = /* @__PURE__ */ new Map();
|
|
1994
|
+
constructor(owner) {
|
|
1995
|
+
this.owner = owner;
|
|
1996
|
+
}
|
|
1997
|
+
seen(store, blob) {
|
|
1998
|
+
this.byStore.set(store, markerKeysOf(blob, this.owner));
|
|
1999
|
+
}
|
|
2000
|
+
wrote(store, patch) {
|
|
2001
|
+
const keys = this.byStore.get(store);
|
|
2002
|
+
if (keys === void 0) return;
|
|
2003
|
+
const now = markerKeysOf(patch, this.owner);
|
|
2004
|
+
for (const key of Object.keys(patch)) if (now.has(key)) keys.add(key);
|
|
2005
|
+
else keys.delete(key);
|
|
2006
|
+
}
|
|
2007
|
+
mayHoldMarkers(store, patch) {
|
|
2008
|
+
const keys = this.byStore.get(store);
|
|
2009
|
+
if (keys === void 0) return true;
|
|
2010
|
+
return Object.keys(patch).some((key) => keys.has(key));
|
|
2011
|
+
}
|
|
2012
|
+
};
|
|
2013
|
+
function createHandle(raw, opts) {
|
|
1827
2014
|
const chains = /* @__PURE__ */ new Map();
|
|
1828
2015
|
const serialize = (key, fn) => {
|
|
1829
2016
|
const next = (chains.get(key) ?? Promise.resolve()).then(fn, fn);
|
|
@@ -1834,46 +2021,130 @@ function createSecretAwareSettings(raw, opts) {
|
|
|
1834
2021
|
});
|
|
1835
2022
|
return next;
|
|
1836
2023
|
};
|
|
1837
|
-
const
|
|
1838
|
-
|
|
1839
|
-
|
|
2024
|
+
const index = new MarkerKeyIndex(opts.secrets.owner);
|
|
2025
|
+
const resolveRead = async (store, blob) => {
|
|
2026
|
+
index.seen(store, blob);
|
|
2027
|
+
const { value, unresolved } = await resolveSecretMarkers(blob, opts.secrets, {
|
|
2028
|
+
unresolved: "keep-marker",
|
|
2029
|
+
ownedBy: opts.secrets.owner
|
|
2030
|
+
});
|
|
2031
|
+
if (unresolved.length > 0) opts.onUnresolved?.(unresolved);
|
|
2032
|
+
return value;
|
|
2033
|
+
};
|
|
2034
|
+
const write = async (store, declared, scope, patch, readStored, rawWrite) => {
|
|
2035
|
+
if (!touchesTree(declared, patch) && !index.mayHoldMarkers(store, patch)) {
|
|
2036
|
+
await rawWrite(patch);
|
|
2037
|
+
index.wrote(store, patch);
|
|
2038
|
+
return { changed: true };
|
|
2039
|
+
}
|
|
2040
|
+
return serialize(store, async () => {
|
|
1840
2041
|
const stored = await readStored();
|
|
2042
|
+
index.seen(store, stored);
|
|
2043
|
+
const operatorWrite = opts.isOperatorWrite?.() === true;
|
|
2044
|
+
const restored = await restoreUnchangedSecrets({
|
|
2045
|
+
patch,
|
|
2046
|
+
stored,
|
|
2047
|
+
secrets: opts.secrets,
|
|
2048
|
+
operatorWrite
|
|
2049
|
+
});
|
|
1841
2050
|
const result = await transformSecretWrite({
|
|
1842
|
-
tree,
|
|
2051
|
+
tree: widenTreeWithStoredMarkers(declared, stored, Object.keys(restored.patch), opts.secrets.owner),
|
|
1843
2052
|
secrets: opts.secrets,
|
|
1844
|
-
patch,
|
|
2053
|
+
patch: restored.patch,
|
|
1845
2054
|
stored,
|
|
1846
2055
|
...scope !== void 0 ? { scope } : {},
|
|
1847
|
-
operatorWrite
|
|
2056
|
+
operatorWrite
|
|
1848
2057
|
});
|
|
1849
|
-
|
|
2058
|
+
const effective = dropUnchangedKeys(result.patch, stored);
|
|
2059
|
+
const wroteBlob = Object.keys(effective).length > 0;
|
|
2060
|
+
if (wroteBlob) {
|
|
2061
|
+
await rawWrite(effective);
|
|
2062
|
+
index.wrote(store, effective);
|
|
2063
|
+
}
|
|
1850
2064
|
const { orphaned } = await result.afterWrite();
|
|
1851
2065
|
if (orphaned.length > 0) opts.onOrphaned(orphaned);
|
|
2066
|
+
return { changed: wroteBlob || restored.changedRefs.length > 0 };
|
|
1852
2067
|
});
|
|
1853
2068
|
};
|
|
1854
|
-
const
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
2069
|
+
const writeAddon = (patch) => write("addon", opts.addonTree(), void 0, patch, () => readStoredAddonBlob(raw), (p) => raw.writeAddonStore(p));
|
|
2070
|
+
const readAddonStoreResult = raw.readAddonStoreResult;
|
|
2071
|
+
const readAddonStoreKeyResult = raw.readAddonStoreKeyResult;
|
|
2072
|
+
const readDeviceStoreBatch = raw.readDeviceStoreBatch;
|
|
2073
|
+
const overrides = {
|
|
2074
|
+
readAddonStore: async () => resolveRead("addon", await raw.readAddonStore()),
|
|
2075
|
+
...readAddonStoreResult !== void 0 ? { readAddonStoreResult: async () => {
|
|
2076
|
+
const result = await readAddonStoreResult.call(raw);
|
|
2077
|
+
return result.kind === "value" ? {
|
|
2078
|
+
kind: "value",
|
|
2079
|
+
value: await resolveRead("addon", result.value)
|
|
2080
|
+
} : result;
|
|
2081
|
+
} } : {},
|
|
2082
|
+
...readAddonStoreKeyResult !== void 0 ? { readAddonStoreKeyResult: async (key) => {
|
|
2083
|
+
const result = await readAddonStoreKeyResult.call(raw, key);
|
|
2084
|
+
if (result.kind !== "present") return result;
|
|
2085
|
+
const { value, unresolved } = await resolveSecretMarkers({ [key]: result.value }, opts.secrets, {
|
|
2086
|
+
unresolved: "keep-marker",
|
|
2087
|
+
ownedBy: opts.secrets.owner
|
|
2088
|
+
});
|
|
2089
|
+
if (unresolved.length > 0) opts.onUnresolved?.(unresolved);
|
|
2090
|
+
return {
|
|
2091
|
+
kind: "present",
|
|
2092
|
+
value: value[key]
|
|
2093
|
+
};
|
|
2094
|
+
} } : {},
|
|
2095
|
+
readDeviceStore: async (deviceId) => resolveRead(`device:${deviceId}`, await raw.readDeviceStore(deviceId)),
|
|
2096
|
+
...readDeviceStoreBatch !== void 0 ? { readDeviceStoreBatch: async (deviceIds) => {
|
|
2097
|
+
const rows = await readDeviceStoreBatch.call(raw, deviceIds);
|
|
2098
|
+
const out = /* @__PURE__ */ new Map();
|
|
2099
|
+
for (const [deviceId, blob] of rows) out.set(deviceId, await resolveRead(`device:${deviceId}`, blob));
|
|
2100
|
+
return out;
|
|
2101
|
+
} } : {},
|
|
2102
|
+
writeAddonStore: async (patch) => {
|
|
2103
|
+
await writeAddon(patch);
|
|
2104
|
+
},
|
|
2105
|
+
writeDeviceStore: async (deviceId, patch) => {
|
|
2106
|
+
await write(`device:${deviceId}`, opts.deviceTree(), {
|
|
2107
|
+
kind: "device",
|
|
2108
|
+
deviceId
|
|
2109
|
+
}, patch, () => raw.readDeviceStore(deviceId), (p) => raw.writeDeviceStore(deviceId, p));
|
|
2110
|
+
}
|
|
2111
|
+
};
|
|
2112
|
+
return {
|
|
2113
|
+
settings: new Proxy(raw, { get(target, prop, receiver) {
|
|
2114
|
+
if (typeof prop === "string" && Object.prototype.hasOwnProperty.call(overrides, prop)) return Reflect.get(overrides, prop);
|
|
2115
|
+
const value = Reflect.get(target, prop, receiver);
|
|
2116
|
+
return typeof value === "function" ? value.bind(target) : value;
|
|
2117
|
+
} }),
|
|
2118
|
+
raw: {
|
|
2119
|
+
readAddonStore: async () => {
|
|
2120
|
+
const blob = await raw.readAddonStore();
|
|
2121
|
+
index.seen("addon", blob);
|
|
2122
|
+
return blob;
|
|
2123
|
+
},
|
|
2124
|
+
readDeviceStore: async (deviceId) => {
|
|
2125
|
+
const blob = await raw.readDeviceStore(deviceId);
|
|
2126
|
+
index.seen(`device:${deviceId}`, blob);
|
|
2127
|
+
return blob;
|
|
2128
|
+
}
|
|
2129
|
+
},
|
|
2130
|
+
writeAddonStore: writeAddon
|
|
1860
2131
|
};
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
const value = Reflect.get(target, prop, receiver);
|
|
1865
|
-
return typeof value === "function" ? value.bind(target) : value;
|
|
1866
|
-
} });
|
|
1867
|
-
}
|
|
1868
|
-
/** The context the addon sees: identical, except that its settings writes are secret-aware. */
|
|
1869
|
-
function withSecretAwareSettings(context, opts) {
|
|
2132
|
+
}
|
|
2133
|
+
/** The context the addon sees, plus the private raw view and write outcome. */
|
|
2134
|
+
function createSecretAwareContext(context, opts) {
|
|
1870
2135
|
const raw = context.settings;
|
|
1871
|
-
if (raw === void 0) return
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
2136
|
+
if (raw === void 0) return {
|
|
2137
|
+
context,
|
|
2138
|
+
handle: null
|
|
2139
|
+
};
|
|
2140
|
+
const handle = createHandle(raw, opts);
|
|
2141
|
+
return {
|
|
2142
|
+
context: new Proxy(context, { get(target, prop, receiver) {
|
|
2143
|
+
if (prop === "settings") return handle.settings;
|
|
2144
|
+
return Reflect.get(target, prop, receiver);
|
|
2145
|
+
} }),
|
|
2146
|
+
handle
|
|
2147
|
+
};
|
|
1877
2148
|
}
|
|
1878
2149
|
var LEAF = { kind: "leaf" };
|
|
1879
2150
|
var DEFAULT_ITEM_ID_KEY = "id";
|
|
@@ -2061,6 +2332,14 @@ var BaseAddon = class {
|
|
|
2061
2332
|
_secretChangeUnsub = null;
|
|
2062
2333
|
/** >0 while an OPERATOR's settings write is being applied. */
|
|
2063
2334
|
_operatorWriteDepth = 0;
|
|
2335
|
+
/**
|
|
2336
|
+
* The keychain-aware settings handle (D798). Its `raw` view is the ONLY way
|
|
2337
|
+
* to the marker-bearing blobs and stays private to this class: the form
|
|
2338
|
+
* hydrate and config resolution need markers, addon code never does.
|
|
2339
|
+
*/
|
|
2340
|
+
_secretSettings = null;
|
|
2341
|
+
/** Refs an addon-facing read could not resolve — each is logged once per process. */
|
|
2342
|
+
_loggedUnresolvedReads = /* @__PURE__ */ new Set();
|
|
2064
2343
|
constructor(defaults) {
|
|
2065
2344
|
this.defaults = defaults;
|
|
2066
2345
|
this._config = { ...defaults };
|
|
@@ -2097,13 +2376,21 @@ var BaseAddon = class {
|
|
|
2097
2376
|
}
|
|
2098
2377
|
async initialize(context) {
|
|
2099
2378
|
const secrets = context.secrets;
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2379
|
+
if (secrets !== void 0) {
|
|
2380
|
+
const { context: wrapped, handle } = createSecretAwareContext(context, {
|
|
2381
|
+
secrets,
|
|
2382
|
+
addonTree: () => this.addonSecretTree(),
|
|
2383
|
+
deviceTree: () => this.deviceSecretTree(),
|
|
2384
|
+
onOrphaned: (refs) => context.logger.warn("keychain refs dropped by a settings write could not be deleted", { meta: { refs: [...refs] } }),
|
|
2385
|
+
isOperatorWrite: () => this._operatorWriteDepth > 0,
|
|
2386
|
+
onUnresolved: (unresolved) => this.logUnresolvedRead(unresolved)
|
|
2387
|
+
});
|
|
2388
|
+
this._ctx = wrapped;
|
|
2389
|
+
this._secretSettings = handle;
|
|
2390
|
+
} else {
|
|
2391
|
+
this._ctx = context;
|
|
2392
|
+
this._secretSettings = null;
|
|
2393
|
+
}
|
|
2107
2394
|
await this.resolveConfig();
|
|
2108
2395
|
if (secrets !== void 0) this._secretChangeUnsub = secrets.onChange((ref) => {
|
|
2109
2396
|
if (this._configSecretRefs.has(ref)) this.scheduleSecretReresolve(SECRET_CHANGE_DEBOUNCE_MS);
|
|
@@ -2220,11 +2507,8 @@ var BaseAddon = class {
|
|
|
2220
2507
|
async getGlobalSettings(overlay, cap, nodeId) {
|
|
2221
2508
|
const schema = this.globalSettingsSchema(cap);
|
|
2222
2509
|
if (!schema) return { sections: [] };
|
|
2223
|
-
const projected = await this.
|
|
2224
|
-
return hydrateSchema(schema, overlay ? {
|
|
2225
|
-
...projected,
|
|
2226
|
-
...overlay
|
|
2227
|
-
} : projected, { inlineSecretsAsViews: true });
|
|
2510
|
+
const projected = await this.readRawProjectedStore(nodeId, cap);
|
|
2511
|
+
return hydrateSchema(schema, overlay ? await this.overlayKeepingMarkers(projected, overlay) : projected, { inlineSecretsAsViews: true });
|
|
2228
2512
|
}
|
|
2229
2513
|
/**
|
|
2230
2514
|
* The integration-level view of this addon's settings: exactly the sections
|
|
@@ -2269,7 +2553,7 @@ var BaseAddon = class {
|
|
|
2269
2553
|
});
|
|
2270
2554
|
}
|
|
2271
2555
|
if (sections.length === 0) return null;
|
|
2272
|
-
const projected = await this.
|
|
2556
|
+
const projected = await this.readRawProjectedStore(nodeId);
|
|
2273
2557
|
return hydrateSchema({
|
|
2274
2558
|
...schema,
|
|
2275
2559
|
sections
|
|
@@ -2286,12 +2570,50 @@ var BaseAddon = class {
|
|
|
2286
2570
|
* the store for custom option logic (option narrowing, value snapping) to
|
|
2287
2571
|
* read it per-node — never `ctx.settings.readAddonStore()` directly.
|
|
2288
2572
|
* `nodeId` omitted ⇒ the local node (node-blind injection from ctx).
|
|
2573
|
+
*
|
|
2574
|
+
* Keychain values come back RESOLVED (D798): a secret reads as its value,
|
|
2575
|
+
* never as a `{ $secret }` marker. One the keychain cannot serve right now
|
|
2576
|
+
* stays a marker — "stored, unavailable" (`isSecretMarker()` is true, and it
|
|
2577
|
+
* is listed in {@link unresolvedSecrets}) — never `''` and never absent, so
|
|
2578
|
+
* it cannot be mistaken for "nothing stored, mint a new one". Writing the
|
|
2579
|
+
* resolved blob back is a no-op for every unchanged secret.
|
|
2289
2580
|
*/
|
|
2290
2581
|
async resolveGlobalStore(nodeId, cap) {
|
|
2291
2582
|
const raw = await this._ctx?.settings?.readAddonStore() ?? {};
|
|
2583
|
+
return this.projectForNode(raw, nodeId, cap);
|
|
2584
|
+
}
|
|
2585
|
+
projectForNode(store, nodeId, cap) {
|
|
2292
2586
|
const keys = this.perNodeKeys(cap);
|
|
2293
2587
|
const node = normalizeNodeId(nodeId ?? this._ctx?.kernel?.localNodeId);
|
|
2294
|
-
return keys.size > 0 ? projectStore(
|
|
2588
|
+
return keys.size > 0 ? projectStore(store, keys, node) : store;
|
|
2589
|
+
}
|
|
2590
|
+
/** The PROJECTED store with its markers — the form hydrate only (D798). */
|
|
2591
|
+
async readRawProjectedStore(nodeId, cap) {
|
|
2592
|
+
const raw = await this.rawSettings()?.readAddonStore() ?? {};
|
|
2593
|
+
return this.projectForNode(raw, nodeId, cap);
|
|
2594
|
+
}
|
|
2595
|
+
/**
|
|
2596
|
+
* `{ ...stored, ...overlay }`, except that an overlay member equal to the
|
|
2597
|
+
* RESOLVED stored one keeps the stored (marker-bearing) member — so a form
|
|
2598
|
+
* hydrated from a resolved overlay still shows a secret as "set", never as
|
|
2599
|
+
* its value.
|
|
2600
|
+
*/
|
|
2601
|
+
async overlayKeepingMarkers(stored, overlay) {
|
|
2602
|
+
const secrets = this._ctx?.secrets;
|
|
2603
|
+
const merged = {
|
|
2604
|
+
...stored,
|
|
2605
|
+
...overlay
|
|
2606
|
+
};
|
|
2607
|
+
if (secrets === void 0 || collectSecretMarkerRefs(stored).length === 0) return merged;
|
|
2608
|
+
const { value: resolved } = await resolveSecretMarkers(stored, secrets, {
|
|
2609
|
+
unresolved: "keep-marker",
|
|
2610
|
+
ownedBy: secrets.owner
|
|
2611
|
+
});
|
|
2612
|
+
for (const key of Object.keys(overlay)) {
|
|
2613
|
+
if (collectSecretMarkerRefs(stored[key]).length === 0) continue;
|
|
2614
|
+
if (isJsonEqual(overlay[key], resolved[key])) merged[key] = stored[key];
|
|
2615
|
+
}
|
|
2616
|
+
return merged;
|
|
2295
2617
|
}
|
|
2296
2618
|
/**
|
|
2297
2619
|
* Whether ONE global key of this addon's store was written, read three-way
|
|
@@ -2356,8 +2678,12 @@ var BaseAddon = class {
|
|
|
2356
2678
|
async clearSecrets(keys, nodeId) {
|
|
2357
2679
|
const tree = this.addonSecretTree();
|
|
2358
2680
|
const keychain = this._ctx?.secrets !== void 0;
|
|
2681
|
+
const stored = keychain ? await this.readAddonStoreWithRetry() : {};
|
|
2359
2682
|
const patch = {};
|
|
2360
|
-
for (const key of keys)
|
|
2683
|
+
for (const key of keys) {
|
|
2684
|
+
const keychainStored = tree.has(key) || collectSecretMarkerRefs(stored[key]).length > 0;
|
|
2685
|
+
patch[key] = keychain && keychainStored ? SECRET_CLEAR : "";
|
|
2686
|
+
}
|
|
2361
2687
|
await this.applyGlobalPatch(patch, nodeId);
|
|
2362
2688
|
}
|
|
2363
2689
|
async applyGlobalPatch(barePatch, nodeId) {
|
|
@@ -2365,7 +2691,11 @@ var BaseAddon = class {
|
|
|
2365
2691
|
const localNode = normalizeNodeId(this._ctx?.kernel?.localNodeId);
|
|
2366
2692
|
const target = normalizeNodeId(nodeId ?? this._ctx?.kernel?.localNodeId);
|
|
2367
2693
|
const scoped = keys.size > 0 ? scopePatch(barePatch, keys, target) : barePatch;
|
|
2368
|
-
|
|
2694
|
+
const handle = this._secretSettings;
|
|
2695
|
+
if (handle !== null) {
|
|
2696
|
+
const { changed } = await handle.writeAddonStore(scoped);
|
|
2697
|
+
if (!changed) return;
|
|
2698
|
+
} else await this._ctx?.settings?.writeAddonStore(scoped);
|
|
2369
2699
|
if (target !== localNode) return;
|
|
2370
2700
|
await this.resolveConfig();
|
|
2371
2701
|
await this.onConfigChanged();
|
|
@@ -2418,7 +2748,7 @@ var BaseAddon = class {
|
|
|
2418
2748
|
async getDeviceSettings(deviceId) {
|
|
2419
2749
|
const schema = this.deviceSettingsSchema();
|
|
2420
2750
|
if (!schema) return { sections: [] };
|
|
2421
|
-
return hydrateSchema(schema, await this.
|
|
2751
|
+
return hydrateSchema(schema, await this.rawSettings()?.readDeviceStore(deviceId) ?? {}, { inlineSecretsAsViews: true });
|
|
2422
2752
|
}
|
|
2423
2753
|
async updateDeviceSettings(deviceId, patch) {
|
|
2424
2754
|
await this._ctx?.settings?.writeDeviceStore(deviceId, patch);
|
|
@@ -2590,7 +2920,7 @@ var BaseAddon = class {
|
|
|
2590
2920
|
key,
|
|
2591
2921
|
schema,
|
|
2592
2922
|
fallback,
|
|
2593
|
-
read: async () => this.resolveStoredSecrets(await this.
|
|
2923
|
+
read: async () => this.resolveStoredSecrets(await this.rawSettings()?.readDeviceStore(deviceId) ?? {}, false),
|
|
2594
2924
|
write: async (patch) => {
|
|
2595
2925
|
await this._ctx?.settings?.writeDeviceStore(deviceId, patch);
|
|
2596
2926
|
},
|
|
@@ -2652,6 +2982,23 @@ var BaseAddon = class {
|
|
|
2652
2982
|
}
|
|
2653
2983
|
return value;
|
|
2654
2984
|
}
|
|
2985
|
+
/**
|
|
2986
|
+
* The marker-bearing settings reads (D798) — private on purpose. Without a
|
|
2987
|
+
* keychain the context's own view is already raw.
|
|
2988
|
+
*/
|
|
2989
|
+
rawSettings() {
|
|
2990
|
+
return this._secretSettings?.raw ?? this._ctx?.settings;
|
|
2991
|
+
}
|
|
2992
|
+
/** An addon-facing read met markers it could not resolve: say so once per ref. */
|
|
2993
|
+
logUnresolvedRead(unresolved) {
|
|
2994
|
+
const fresh = unresolved.filter((u) => !this._loggedUnresolvedReads.has(u.ref));
|
|
2995
|
+
if (fresh.length === 0) return;
|
|
2996
|
+
for (const u of fresh) this._loggedUnresolvedReads.add(u.ref);
|
|
2997
|
+
this._ctx?.logger?.warn?.("settings read: a stored secret is unavailable — it reads as its marker, not as a value", { meta: { secrets: fresh.map((u) => ({
|
|
2998
|
+
ref: u.ref,
|
|
2999
|
+
reason: u.reason
|
|
3000
|
+
})) } });
|
|
3001
|
+
}
|
|
2655
3002
|
/** Re-resolve the config (a keychain-only change, or a retry) and apply it. */
|
|
2656
3003
|
scheduleSecretReresolve(delayMs) {
|
|
2657
3004
|
const timerOf = delayMs === SECRET_CHANGE_DEBOUNCE_MS ? "_secretChangeTimer" : "_secretRetryTimer";
|
|
@@ -2678,7 +3025,7 @@ var BaseAddon = class {
|
|
|
2678
3025
|
* `{}` (defaults) — the addon's first successful patch will rehydrate.
|
|
2679
3026
|
*/
|
|
2680
3027
|
async readAddonStoreWithRetry() {
|
|
2681
|
-
const settings = this.
|
|
3028
|
+
const settings = this.rawSettings();
|
|
2682
3029
|
if (!settings) return {};
|
|
2683
3030
|
const delaysMs = [
|
|
2684
3031
|
150,
|
|
@@ -3796,6 +4143,28 @@ var adminUiCapability = {
|
|
|
3796
4143
|
getVersion: method(z.void(), VersionOutputSchema$1, { auth: "admin" })
|
|
3797
4144
|
}
|
|
3798
4145
|
};
|
|
4146
|
+
/**
|
|
4147
|
+
* Identity — preserves literal types for downstream inference.
|
|
4148
|
+
*
|
|
4149
|
+
* The constraint is `Record<string, unknown>` (not `CustomActionsSpec`) so
|
|
4150
|
+
* TypeScript does not widen each entry's literal `kind`/`auth` fields to
|
|
4151
|
+
* the broader unions declared on `CustomActionSpec`'s default generics.
|
|
4152
|
+
* Shape validity is enforced separately by the `customAction(...)` helper
|
|
4153
|
+
* whose return type is already a `CustomActionSpec<...>`.
|
|
4154
|
+
*/
|
|
4155
|
+
function defineCustomActions(spec) {
|
|
4156
|
+
return spec;
|
|
4157
|
+
}
|
|
4158
|
+
function customAction(input, output, options) {
|
|
4159
|
+
return {
|
|
4160
|
+
input,
|
|
4161
|
+
output,
|
|
4162
|
+
kind: options?.kind ?? "query",
|
|
4163
|
+
auth: options?.auth ?? "protected",
|
|
4164
|
+
scope: options?.scope ?? { kind: "system" },
|
|
4165
|
+
...options?.caller ? { caller: "required" } : {}
|
|
4166
|
+
};
|
|
4167
|
+
}
|
|
3799
4168
|
var StaticDirOutputSchema = z.object({ staticDir: z.string() });
|
|
3800
4169
|
var VersionOutputSchema = z.object({ version: z.string() });
|
|
3801
4170
|
/**
|
|
@@ -6652,6 +7021,278 @@ var DERIVED_CAMERA_SILENCED_CAP_NAMES = [
|
|
|
6652
7021
|
"scene-monitor"
|
|
6653
7022
|
];
|
|
6654
7023
|
/**
|
|
7024
|
+
* The trainer contract — what the hub hands the cloud trainer and what it
|
|
7025
|
+
* gets back (cloud training, D805+).
|
|
7026
|
+
*
|
|
7027
|
+
* The trainer (`hf/camstack-trainer`, published as the Hugging Face repo
|
|
7028
|
+
* `camstack/camstack-trainer`) is a separate AGPL program the hub never
|
|
7029
|
+
* imports: `job.json` goes in, files come out, and progress is `CAMSTACK_EVENT`
|
|
7030
|
+
* lines on stdout. These schemas are the hub's half of that boundary; the
|
|
7031
|
+
* trainer's pydantic models (and the JSON Schemas generated from them under
|
|
7032
|
+
* `hf/camstack-trainer/schemas/`) are the other half. A change here is a
|
|
7033
|
+
* change there, in the same piece of work.
|
|
7034
|
+
*
|
|
7035
|
+
* No `.default()` anywhere: defaults do not run on the addon cap path, so the
|
|
7036
|
+
* orchestrator fills every field of a job spec explicitly.
|
|
7037
|
+
*/
|
|
7038
|
+
var TRAINING_JOB_SCHEMA = "camstack.training/v1";
|
|
7039
|
+
var TRAINING_RESULT_SCHEMA = "camstack.training-result/v1";
|
|
7040
|
+
/** What the trainer can export. Edge TPU is a later, separate image. */
|
|
7041
|
+
var TrainingExportSchema = z.enum([
|
|
7042
|
+
"onnx",
|
|
7043
|
+
"openvino-fp16",
|
|
7044
|
+
"openvino-int8",
|
|
7045
|
+
"coreml-fp16"
|
|
7046
|
+
]);
|
|
7047
|
+
var FloorsSchema = z.record(z.string(), z.number().min(0).max(1));
|
|
7048
|
+
z.object({
|
|
7049
|
+
schema: z.literal(TRAINING_JOB_SCHEMA),
|
|
7050
|
+
jobKey: z.string().uuid(),
|
|
7051
|
+
target: z.object({
|
|
7052
|
+
kind: z.literal("detector"),
|
|
7053
|
+
step: z.literal("object-detection")
|
|
7054
|
+
}),
|
|
7055
|
+
base: z.object({
|
|
7056
|
+
modelId: z.string().min(1),
|
|
7057
|
+
/** Ultralytics weights id, e.g. `yolov9m.pt`. */
|
|
7058
|
+
weights: z.string().min(1),
|
|
7059
|
+
imgsz: z.number().int().positive()
|
|
7060
|
+
}),
|
|
7061
|
+
baseline: z.object({
|
|
7062
|
+
modelId: z.string().min(1),
|
|
7063
|
+
/** The baseline ONNX the holdout comparison runs; public mirror URL. */
|
|
7064
|
+
onnxUrl: z.string().url(),
|
|
7065
|
+
imgsz: z.number().int().positive(),
|
|
7066
|
+
floors: FloorsSchema
|
|
7067
|
+
}),
|
|
7068
|
+
dataset: z.object({
|
|
7069
|
+
versionId: z.string().min(1),
|
|
7070
|
+
annotationHash: z.string(),
|
|
7071
|
+
/** sha256 of the uploaded archive; the trainer refuses a mismatch (exit 65). */
|
|
7072
|
+
sha256: z.string().regex(/^[0-9a-f]{64}$/)
|
|
7073
|
+
}),
|
|
7074
|
+
split: z.object({
|
|
7075
|
+
holdoutCameras: z.array(z.number().int()),
|
|
7076
|
+
/** When no camera is held out: the last N days of every camera. */
|
|
7077
|
+
holdoutLastDays: z.number().int().nonnegative(),
|
|
7078
|
+
valFraction: z.number().min(0).max(.5),
|
|
7079
|
+
seed: z.number().int()
|
|
7080
|
+
}),
|
|
7081
|
+
hyper: z.object({
|
|
7082
|
+
epochs: z.number().int().positive(),
|
|
7083
|
+
batch: z.number().int().positive(),
|
|
7084
|
+
lr0: z.number().positive(),
|
|
7085
|
+
/** Layers frozen from the start of the backbone; 0 = none. */
|
|
7086
|
+
freeze: z.number().int().nonnegative(),
|
|
7087
|
+
patience: z.number().int().nonnegative(),
|
|
7088
|
+
seed: z.number().int()
|
|
7089
|
+
}),
|
|
7090
|
+
replay: z.object({
|
|
7091
|
+
source: z.literal("coco2017"),
|
|
7092
|
+
count: z.number().int().nonnegative()
|
|
7093
|
+
}),
|
|
7094
|
+
exports: z.array(TrainingExportSchema).min(1),
|
|
7095
|
+
calibration: z.object({ maxFrames: z.number().int().positive() }),
|
|
7096
|
+
floors: z.object({
|
|
7097
|
+
minSupport: z.number().int().positive(),
|
|
7098
|
+
clamp: z.number().min(0).max(1)
|
|
7099
|
+
}),
|
|
7100
|
+
int8Gate: z.object({ maxMap50Drop: z.number().min(0).max(1) }),
|
|
7101
|
+
catalog: z.object({
|
|
7102
|
+
id: z.string().regex(/^[a-z0-9][a-z0-9-]{2,80}$/),
|
|
7103
|
+
name: z.string().min(1).max(160)
|
|
7104
|
+
})
|
|
7105
|
+
});
|
|
7106
|
+
/** One regular file of the bundle. An mlpackage contributes one row per member. */
|
|
7107
|
+
var TrainingResultFileSchema = z.object({
|
|
7108
|
+
path: z.string().min(1),
|
|
7109
|
+
bytes: z.number().int().nonnegative(),
|
|
7110
|
+
sha256: z.string().regex(/^[0-9a-f]{64}$/),
|
|
7111
|
+
format: z.enum([
|
|
7112
|
+
"onnx",
|
|
7113
|
+
"openvino",
|
|
7114
|
+
"coreml",
|
|
7115
|
+
"pt"
|
|
7116
|
+
]),
|
|
7117
|
+
variant: z.enum(["base", "int8"])
|
|
7118
|
+
});
|
|
7119
|
+
var DraftFormatSchema = z.object({
|
|
7120
|
+
/** RELATIVE path inside the bundle; the hub rewrites it to `camstack-local://`. */
|
|
7121
|
+
url: z.string().min(1),
|
|
7122
|
+
sizeMB: z.number().nonnegative(),
|
|
7123
|
+
isDirectory: z.boolean().optional(),
|
|
7124
|
+
files: z.array(z.string()).optional(),
|
|
7125
|
+
runtimes: z.array(z.literal("python")).optional(),
|
|
7126
|
+
revision: z.string().min(1)
|
|
7127
|
+
});
|
|
7128
|
+
/** The trainer's half of a catalog entry; the hub completes it (labels, licence, group). */
|
|
7129
|
+
var TrainingCatalogDraftSchema = z.object({
|
|
7130
|
+
id: z.string().min(1),
|
|
7131
|
+
name: z.string().min(1),
|
|
7132
|
+
description: z.string(),
|
|
7133
|
+
labels: z.array(z.unknown()),
|
|
7134
|
+
inputSize: z.object({
|
|
7135
|
+
width: z.number().int().positive(),
|
|
7136
|
+
height: z.number().int().positive()
|
|
7137
|
+
}),
|
|
7138
|
+
preprocessMode: z.literal("letterbox"),
|
|
7139
|
+
defaultClassFloors: FloorsSchema,
|
|
7140
|
+
formats: z.object({
|
|
7141
|
+
onnx: DraftFormatSchema.optional(),
|
|
7142
|
+
openvino: DraftFormatSchema.optional(),
|
|
7143
|
+
coreml: DraftFormatSchema.optional()
|
|
7144
|
+
})
|
|
7145
|
+
});
|
|
7146
|
+
/** One class of one model's holdout report (the eval harness's summary). */
|
|
7147
|
+
var TrainingEvalClassSchema = z.object({
|
|
7148
|
+
positives: z.number().int(),
|
|
7149
|
+
floor: z.number().nullable(),
|
|
7150
|
+
tp: z.number().int(),
|
|
7151
|
+
fp: z.number().int(),
|
|
7152
|
+
fn: z.number().int(),
|
|
7153
|
+
precision: z.number().nullable(),
|
|
7154
|
+
recall: z.number().nullable(),
|
|
7155
|
+
ap50: z.number().nullable(),
|
|
7156
|
+
recallByCamera: z.record(z.string(), z.number().nullable()).optional(),
|
|
7157
|
+
recallByLight: z.record(z.string(), z.number().nullable()).optional(),
|
|
7158
|
+
fpByCamera: z.record(z.string(), z.number()).optional(),
|
|
7159
|
+
smallFound: z.number().int().optional(),
|
|
7160
|
+
smallTotal: z.number().int().optional()
|
|
7161
|
+
}).passthrough();
|
|
7162
|
+
var TrainingEvalReportSchema = z.object({
|
|
7163
|
+
frames: z.number().int(),
|
|
7164
|
+
classes: z.record(z.string(), TrainingEvalClassSchema)
|
|
7165
|
+
}).passthrough();
|
|
7166
|
+
/**
|
|
7167
|
+
* A succeeded result carries every block; a failed one carries the error and
|
|
7168
|
+
* whatever was known by then — the other blocks are ABSENT, not null. The
|
|
7169
|
+
* orchestrator checks the blocks it needs before it trusts a success.
|
|
7170
|
+
*/
|
|
7171
|
+
var TrainingResultSchema = z.object({
|
|
7172
|
+
schema: z.literal(TRAINING_RESULT_SCHEMA),
|
|
7173
|
+
jobKey: z.string(),
|
|
7174
|
+
status: z.enum(["succeeded", "failed"]),
|
|
7175
|
+
error: z.object({
|
|
7176
|
+
stage: z.string(),
|
|
7177
|
+
code: z.string(),
|
|
7178
|
+
message: z.string()
|
|
7179
|
+
}).nullable(),
|
|
7180
|
+
trainer: z.object({
|
|
7181
|
+
version: z.string(),
|
|
7182
|
+
revision: z.string()
|
|
7183
|
+
}),
|
|
7184
|
+
toolchain: z.record(z.string(), z.string().nullable()),
|
|
7185
|
+
dataset: z.object({
|
|
7186
|
+
versionId: z.string(),
|
|
7187
|
+
annotationHash: z.string(),
|
|
7188
|
+
sha256: z.string(),
|
|
7189
|
+
frames: z.object({
|
|
7190
|
+
train: z.number().int(),
|
|
7191
|
+
val: z.number().int(),
|
|
7192
|
+
holdout: z.number().int()
|
|
7193
|
+
}),
|
|
7194
|
+
holdoutCameras: z.array(z.number().int()),
|
|
7195
|
+
replayImages: z.number().int()
|
|
7196
|
+
}).optional(),
|
|
7197
|
+
training: z.object({
|
|
7198
|
+
epochsRun: z.number().int(),
|
|
7199
|
+
bestEpoch: z.number().int(),
|
|
7200
|
+
bestMap50: z.number(),
|
|
7201
|
+
bestMap50_95: z.number(),
|
|
7202
|
+
seconds: z.number()
|
|
7203
|
+
}).optional(),
|
|
7204
|
+
evaluation: z.object({
|
|
7205
|
+
holdoutFrames: z.number().int(),
|
|
7206
|
+
baseline: TrainingEvalReportSchema.nullable(),
|
|
7207
|
+
candidate: TrainingEvalReportSchema.nullable(),
|
|
7208
|
+
candidateInt8: TrainingEvalReportSchema.nullable(),
|
|
7209
|
+
recallDeltaByCamera: z.record(z.string(), z.record(z.string(), z.number())).nullable(),
|
|
7210
|
+
map50: z.record(z.string(), z.number().nullable())
|
|
7211
|
+
}).optional(),
|
|
7212
|
+
floors: z.object({
|
|
7213
|
+
baseline: FloorsSchema,
|
|
7214
|
+
candidate: FloorsSchema,
|
|
7215
|
+
candidateInt8: FloorsSchema.nullable(),
|
|
7216
|
+
notes: z.array(z.string())
|
|
7217
|
+
}).optional(),
|
|
7218
|
+
int8: z.object({
|
|
7219
|
+
kept: z.boolean(),
|
|
7220
|
+
map50Fp16: z.number().nullable(),
|
|
7221
|
+
map50Int8: z.number().nullable(),
|
|
7222
|
+
reason: z.string().nullable()
|
|
7223
|
+
}).optional(),
|
|
7224
|
+
exportsDropped: z.array(z.object({
|
|
7225
|
+
export: z.string(),
|
|
7226
|
+
reason: z.string()
|
|
7227
|
+
})).optional(),
|
|
7228
|
+
files: z.array(TrainingResultFileSchema).optional(),
|
|
7229
|
+
bundle: z.object({
|
|
7230
|
+
path: z.literal("bundle.tar"),
|
|
7231
|
+
bytes: z.number().int().nonnegative(),
|
|
7232
|
+
sha256: z.string().regex(/^[0-9a-f]{64}$/)
|
|
7233
|
+
}).nullable().optional(),
|
|
7234
|
+
catalogDraft: z.object({
|
|
7235
|
+
base: TrainingCatalogDraftSchema,
|
|
7236
|
+
int8: TrainingCatalogDraftSchema.nullable()
|
|
7237
|
+
}).nullable().optional(),
|
|
7238
|
+
licence: z.string()
|
|
7239
|
+
});
|
|
7240
|
+
var TrainingStageSchema = z.enum([
|
|
7241
|
+
"prepare",
|
|
7242
|
+
"replay",
|
|
7243
|
+
"train",
|
|
7244
|
+
"eval",
|
|
7245
|
+
"export",
|
|
7246
|
+
"package"
|
|
7247
|
+
]);
|
|
7248
|
+
var EventBase = {
|
|
7249
|
+
v: z.literal(1),
|
|
7250
|
+
ts: z.number()
|
|
7251
|
+
};
|
|
7252
|
+
z.discriminatedUnion("type", [
|
|
7253
|
+
z.object({
|
|
7254
|
+
...EventBase,
|
|
7255
|
+
type: z.literal("stage"),
|
|
7256
|
+
stage: TrainingStageSchema
|
|
7257
|
+
}),
|
|
7258
|
+
z.object({
|
|
7259
|
+
...EventBase,
|
|
7260
|
+
type: z.literal("epoch"),
|
|
7261
|
+
epoch: z.number().int(),
|
|
7262
|
+
epochs: z.number().int(),
|
|
7263
|
+
boxLoss: z.number().nullable().optional(),
|
|
7264
|
+
map50: z.number().nullable().optional(),
|
|
7265
|
+
map50_95: z.number().nullable().optional(),
|
|
7266
|
+
etaSec: z.number().nullable().optional()
|
|
7267
|
+
}),
|
|
7268
|
+
z.object({
|
|
7269
|
+
...EventBase,
|
|
7270
|
+
type: z.literal("metric"),
|
|
7271
|
+
name: z.string(),
|
|
7272
|
+
value: z.number()
|
|
7273
|
+
}),
|
|
7274
|
+
z.object({
|
|
7275
|
+
...EventBase,
|
|
7276
|
+
type: z.literal("artifact"),
|
|
7277
|
+
path: z.string(),
|
|
7278
|
+
bytes: z.number(),
|
|
7279
|
+
sha256: z.string()
|
|
7280
|
+
}),
|
|
7281
|
+
z.object({
|
|
7282
|
+
...EventBase,
|
|
7283
|
+
type: z.literal("done"),
|
|
7284
|
+
status: z.enum(["succeeded", "failed"]),
|
|
7285
|
+
resultSha256: z.string().nullable().optional()
|
|
7286
|
+
}),
|
|
7287
|
+
z.object({
|
|
7288
|
+
...EventBase,
|
|
7289
|
+
type: z.literal("error"),
|
|
7290
|
+
stage: z.string(),
|
|
7291
|
+
code: z.string(),
|
|
7292
|
+
message: z.string()
|
|
7293
|
+
})
|
|
7294
|
+
]);
|
|
7295
|
+
/**
|
|
6655
7296
|
* What the gate decided about a birth, from the CALLER's point of view.
|
|
6656
7297
|
*
|
|
6657
7298
|
* Deliberately not `ConfirmationVerdict`: `undecided` is not a birth decision
|
|
@@ -9408,7 +10049,9 @@ var AddonHttpRouteSchema = z.object({
|
|
|
9408
10049
|
"authenticated",
|
|
9409
10050
|
"admin"
|
|
9410
10051
|
]).optional(),
|
|
9411
|
-
description: z.string().optional()
|
|
10052
|
+
description: z.string().optional(),
|
|
10053
|
+
/** The hub limits this route per client address before the handler runs. */
|
|
10054
|
+
rateLimit: z.enum(["auth"]).optional()
|
|
9412
10055
|
});
|
|
9413
10056
|
/**
|
|
9414
10057
|
* Cross-process route invocation envelope. The hub captures the
|
|
@@ -10372,6 +11015,37 @@ var BackupDestinationInfoSchema = z.object({
|
|
|
10372
11015
|
lastError: z.string().optional()
|
|
10373
11016
|
});
|
|
10374
11017
|
/**
|
|
11018
|
+
* How an archive is encrypted (operator decision 2026-10-09): to a backup
|
|
11019
|
+
* PUBLIC key the hub holds, so only the operator's secret key opens it.
|
|
11020
|
+
* Absent on a plain `.tar.gz` — every archive made before encryption, or
|
|
11021
|
+
* with it off — which restores without a key.
|
|
11022
|
+
*/
|
|
11023
|
+
var BackupArchiveEncryptionSchema = z.object({
|
|
11024
|
+
/** `x25519-hkdf-aes256gcm-stream-v1`. */
|
|
11025
|
+
scheme: z.string(),
|
|
11026
|
+
/** 16 hex — the backup key whose secret restores this archive. */
|
|
11027
|
+
keyId: z.string()
|
|
11028
|
+
});
|
|
11029
|
+
/**
|
|
11030
|
+
* The backup encryption setting. The hub keeps PUBLIC keys only: the active
|
|
11031
|
+
* one new archives are encrypted to, and the retired ones older archives
|
|
11032
|
+
* still need. The secret key is shown once, at `setupEncryption`.
|
|
11033
|
+
*/
|
|
11034
|
+
var BackupEncryptionStatusSchema = z.object({
|
|
11035
|
+
enabled: z.boolean(),
|
|
11036
|
+
active: z.object({
|
|
11037
|
+
keyId: z.string(),
|
|
11038
|
+
publicKey: z.string(),
|
|
11039
|
+
createdAt: z.number()
|
|
11040
|
+
}).nullable(),
|
|
11041
|
+
/** Every key ever set up, newest first. */
|
|
11042
|
+
keys: z.array(z.object({
|
|
11043
|
+
keyId: z.string(),
|
|
11044
|
+
createdAt: z.number(),
|
|
11045
|
+
retiredAt: z.number().nullable()
|
|
11046
|
+
})).readonly()
|
|
11047
|
+
});
|
|
11048
|
+
/**
|
|
10375
11049
|
* Per-archive entry returned by `backup.listArchives({ destinationId })`.
|
|
10376
11050
|
* Same shape the destination drill-in renders. Sourced from the per-
|
|
10377
11051
|
* location `manifests.json` (no per-archive tarball read required).
|
|
@@ -10383,7 +11057,9 @@ var BackupArchiveEntrySchema = z.object({
|
|
|
10383
11057
|
sizeBytes: z.number(),
|
|
10384
11058
|
label: z.string().optional(),
|
|
10385
11059
|
/** Top-level locations included in the archive (db, addons, tls, …). */
|
|
10386
|
-
locations: z.array(z.string()).readonly()
|
|
11060
|
+
locations: z.array(z.string()).readonly(),
|
|
11061
|
+
/** Present when the archive is encrypted; restore then needs that key. */
|
|
11062
|
+
encryption: BackupArchiveEncryptionSchema.optional()
|
|
10387
11063
|
});
|
|
10388
11064
|
var BackupEntrySchema = z.object({
|
|
10389
11065
|
id: z.string(),
|
|
@@ -10392,7 +11068,8 @@ var BackupEntrySchema = z.object({
|
|
|
10392
11068
|
label: z.string().optional(),
|
|
10393
11069
|
createdAt: z.number(),
|
|
10394
11070
|
sizeBytes: z.number(),
|
|
10395
|
-
locations: z.array(z.string()).optional()
|
|
11071
|
+
locations: z.array(z.string()).optional(),
|
|
11072
|
+
encryption: BackupArchiveEncryptionSchema.optional()
|
|
10396
11073
|
});
|
|
10397
11074
|
var ArchiveEntrySchema = z.object({
|
|
10398
11075
|
path: z.string(),
|
|
@@ -10647,11 +11324,58 @@ var backupCapability = {
|
|
|
10647
11324
|
* this list and skips entries whose path doesn't start with
|
|
10648
11325
|
* any of them.
|
|
10649
11326
|
*/
|
|
10650
|
-
locations: z.array(z.string()).optional()
|
|
11327
|
+
locations: z.array(z.string()).optional(),
|
|
11328
|
+
/**
|
|
11329
|
+
* The backup SECRET key, for an encrypted archive (ignored for a plain
|
|
11330
|
+
* one). The archive is decrypted into the hub's private staging
|
|
11331
|
+
* before the restart; the key is not stored. A wrong or missing key
|
|
11332
|
+
* is refused before the archive is downloaded.
|
|
11333
|
+
*/
|
|
11334
|
+
backupSecretKey: z.string().optional()
|
|
10651
11335
|
}), z.void(), {
|
|
10652
11336
|
kind: "mutation",
|
|
10653
11337
|
auth: "admin"
|
|
10654
11338
|
}),
|
|
11339
|
+
/** The encryption setting — public keys only. */
|
|
11340
|
+
getEncryption: method(z.void(), BackupEncryptionStatusSchema, { auth: "admin" }),
|
|
11341
|
+
/**
|
|
11342
|
+
* Turn encryption on, or replace the key: generates a new key pair, keeps
|
|
11343
|
+
* the PUBLIC key, and returns the SECRET key — once. It is never stored
|
|
11344
|
+
* on the hub and cannot be shown again; archives made from now on need
|
|
11345
|
+
* it to be restored. A previous key is retired: archives made with it
|
|
11346
|
+
* still need IT.
|
|
11347
|
+
*/
|
|
11348
|
+
setupEncryption: method(z.object({}), z.object({
|
|
11349
|
+
keyId: z.string(),
|
|
11350
|
+
secretKey: z.string(),
|
|
11351
|
+
publicKey: z.string(),
|
|
11352
|
+
createdAt: z.number(),
|
|
11353
|
+
replacedKeyId: z.string().nullable()
|
|
11354
|
+
}), {
|
|
11355
|
+
kind: "mutation",
|
|
11356
|
+
auth: "admin"
|
|
11357
|
+
}),
|
|
11358
|
+
/** New archives are plain again. Existing encrypted archives keep needing their key. */
|
|
11359
|
+
disableEncryption: method(z.object({}), z.object({ disabledKeyId: z.string().nullable() }), {
|
|
11360
|
+
kind: "mutation",
|
|
11361
|
+
auth: "admin"
|
|
11362
|
+
}),
|
|
11363
|
+
/**
|
|
11364
|
+
* Does this secret key parse, and which backup key is it? A mutation so
|
|
11365
|
+
* the key never travels in a URL. Nothing is stored.
|
|
11366
|
+
*/
|
|
11367
|
+
checkBackupKey: method(z.object({ backupSecretKey: z.string() }), z.object({
|
|
11368
|
+
valid: z.boolean(),
|
|
11369
|
+
keyId: z.string().nullable(),
|
|
11370
|
+
/** The hub knows this key (active or retired). */
|
|
11371
|
+
known: z.boolean(),
|
|
11372
|
+
/** It is the key new archives are encrypted to. */
|
|
11373
|
+
active: z.boolean(),
|
|
11374
|
+
error: z.string().optional()
|
|
11375
|
+
}), {
|
|
11376
|
+
kind: "mutation",
|
|
11377
|
+
auth: "admin"
|
|
11378
|
+
}),
|
|
10655
11379
|
delete: method(z.object({
|
|
10656
11380
|
destinationId: z.string(),
|
|
10657
11381
|
backupId: z.string()
|
|
@@ -14162,22 +14886,87 @@ var CustomModelDescriptorSchema = z.object({
|
|
|
14162
14886
|
stepId: z.string(),
|
|
14163
14887
|
entry: ModelCatalogEntrySchema
|
|
14164
14888
|
});
|
|
14889
|
+
/** One regular file of an imported bundle, as the producer measured it. */
|
|
14890
|
+
var ModelBundleFileSchema = z.object({
|
|
14891
|
+
/** Relative path inside the bundle tar; an mlpackage member is `<x>.mlpackage/<member>`. */
|
|
14892
|
+
path: z.string().min(1),
|
|
14893
|
+
bytes: z.number().int().nonnegative(),
|
|
14894
|
+
sha256: z.string().regex(/^[0-9a-f]{64}$/)
|
|
14895
|
+
});
|
|
14896
|
+
var ImportModelBundleInputSchema = z.object({
|
|
14897
|
+
/** The registry provider that keeps the model (Model Studio). */
|
|
14898
|
+
addonId: z.string(),
|
|
14899
|
+
stepId: z.string().min(1),
|
|
14900
|
+
/**
|
|
14901
|
+
* The catalog rows to register, complete, every format URL already
|
|
14902
|
+
* `camstack-local://<modelId>/<file>` naming a file of the bundle. One
|
|
14903
|
+
* bundle may carry several rows (a base and its INT8 variant).
|
|
14904
|
+
*/
|
|
14905
|
+
entries: z.array(ModelCatalogEntrySchema).min(1).max(4),
|
|
14906
|
+
/** The bundle tar, offered by the producer over `ctx.peerBytes` (same host). */
|
|
14907
|
+
bundle: z.object({
|
|
14908
|
+
ticket: PeerBytesTicketSchema,
|
|
14909
|
+
bytes: z.number().int().positive(),
|
|
14910
|
+
sha256: z.string().regex(/^[0-9a-f]{64}$/)
|
|
14911
|
+
}),
|
|
14912
|
+
/** Every file the tar must hold; anything missing, extra or different is refused. */
|
|
14913
|
+
files: z.array(ModelBundleFileSchema).min(1).max(64),
|
|
14914
|
+
/** Convert missing engine formats and push the model to every agent afterwards. */
|
|
14915
|
+
distribute: z.boolean()
|
|
14916
|
+
});
|
|
14917
|
+
var ImportModelBundleResultSchema = z.discriminatedUnion("kind", [z.object({
|
|
14918
|
+
kind: z.literal("imported"),
|
|
14919
|
+
modelIds: z.array(z.string()).readonly(),
|
|
14920
|
+
distributed: z.array(z.object({
|
|
14921
|
+
modelId: z.string(),
|
|
14922
|
+
nodeId: z.string(),
|
|
14923
|
+
format: z.enum(MODEL_FORMATS),
|
|
14924
|
+
ok: z.boolean(),
|
|
14925
|
+
error: z.string().optional()
|
|
14926
|
+
})).readonly()
|
|
14927
|
+
}), z.object({
|
|
14928
|
+
kind: z.literal("refused"),
|
|
14929
|
+
reason: z.enum([
|
|
14930
|
+
"transfer-failed",
|
|
14931
|
+
"checksum-mismatch",
|
|
14932
|
+
"bundle-mismatch",
|
|
14933
|
+
"invalid-entry",
|
|
14934
|
+
"id-taken"
|
|
14935
|
+
]),
|
|
14936
|
+
detail: z.string()
|
|
14937
|
+
})]);
|
|
14165
14938
|
var customModelRegistryCapability = {
|
|
14166
14939
|
name: "custom-model-registry",
|
|
14167
14940
|
scope: "system",
|
|
14168
14941
|
mode: "collection",
|
|
14169
14942
|
internal: true,
|
|
14170
|
-
methods: {
|
|
14171
|
-
|
|
14172
|
-
|
|
14173
|
-
|
|
14174
|
-
|
|
14175
|
-
|
|
14176
|
-
|
|
14177
|
-
|
|
14178
|
-
|
|
14179
|
-
|
|
14180
|
-
listModels: method(z.void(), z.array(CustomModelDescriptorSchema).readonly(), { auth: "admin" })
|
|
14943
|
+
methods: {
|
|
14944
|
+
/**
|
|
14945
|
+
* `internal: true` did not gate the mount (see the 2026-08-26 note on
|
|
14946
|
+
* `data-store-provider`) — this was reachable on the AppRouter by ANY
|
|
14947
|
+
* authenticated session at the default `auth: 'protected'`. The only
|
|
14948
|
+
* consumer, the detection-pipeline's model resolution
|
|
14949
|
+
* (`addon-pipeline/src/detection-pipeline/provider.ts`), calls
|
|
14950
|
+
* `ctx.api.customModelRegistry.listModels.query()` — UDS/Moleculer,
|
|
14951
|
+
* never tRPC — so `auth: 'admin'` costs it nothing.
|
|
14952
|
+
*/
|
|
14953
|
+
listModels: method(z.void(), z.array(CustomModelDescriptorSchema).readonly(), { auth: "admin" }),
|
|
14954
|
+
/**
|
|
14955
|
+
* Register a model whose files arrive as ONE bundle tar (D806): the tar
|
|
14956
|
+
* is taken over `ctx.peerBytes`, checked against `bundle.sha256` and
|
|
14957
|
+
* every row of `files`, unpacked into the hub's flat modelsDir (directory
|
|
14958
|
+
* formats included), each format's `.revision` record written, the rows
|
|
14959
|
+
* upserted under the operator's name, the hub marked available and —
|
|
14960
|
+
* when asked — engine formats converted and the model distributed. A
|
|
14961
|
+
* model id already registered by another source is refused, never
|
|
14962
|
+
* overwritten.
|
|
14963
|
+
*/
|
|
14964
|
+
importModelBundle: method(ImportModelBundleInputSchema, ImportModelBundleResultSchema, {
|
|
14965
|
+
kind: "mutation",
|
|
14966
|
+
auth: "admin",
|
|
14967
|
+
timeoutMs: 30 * 6e4
|
|
14968
|
+
})
|
|
14969
|
+
}
|
|
14181
14970
|
};
|
|
14182
14971
|
/**
|
|
14183
14972
|
* Query filter for settings-store collections.
|
|
@@ -14984,6 +15773,42 @@ var KeychainKeySourceSchema = z.object({
|
|
|
14984
15773
|
*/
|
|
14985
15774
|
sharesBackupWithDatabase: z.boolean()
|
|
14986
15775
|
});
|
|
15776
|
+
/**
|
|
15777
|
+
* Who reset the keychain: an operator from System → Secrets (`user:<id>`), or
|
|
15778
|
+
* the one-shot `<dataDir>/.keychain/RESET_KEYCHAIN` trigger file at boot
|
|
15779
|
+
* (`trigger-file`) — the only way back into a hub whose master key is lost,
|
|
15780
|
+
* because a locked keychain leaves no session that could reach the page (D797).
|
|
15781
|
+
*/
|
|
15782
|
+
var KEYCHAIN_RESET_TRIGGER_ACTOR = "trigger-file";
|
|
15783
|
+
/**
|
|
15784
|
+
* One keychain reset, as the Secrets page reports it until the operator
|
|
15785
|
+
* acknowledges it. Never a value: refs, counts and table names only.
|
|
15786
|
+
*/
|
|
15787
|
+
var KeychainResetRecordSchema = z.object({
|
|
15788
|
+
id: z.string(),
|
|
15789
|
+
at: z.number(),
|
|
15790
|
+
/** `user:<id>` or {@link KEYCHAIN_RESET_TRIGGER_ACTOR}. */
|
|
15791
|
+
actor: z.string(),
|
|
15792
|
+
/** Why the keychain was locked when it was reset; `null` when it was not locked. */
|
|
15793
|
+
lockedReason: z.string().nullable(),
|
|
15794
|
+
/** Values moved into the archive table (ciphertext under the lost key). */
|
|
15795
|
+
archived: z.number().int(),
|
|
15796
|
+
/** `keychain:secrets-orphaned-<ts>`, or `null` when nothing was stored. */
|
|
15797
|
+
archivedCollection: z.string().nullable(),
|
|
15798
|
+
/**
|
|
15799
|
+
* `keychain:keys-orphaned-<ts>`: the wrapped data keys of the archive, kept
|
|
15800
|
+
* so the archive can still be opened if the lost master key turns up.
|
|
15801
|
+
*/
|
|
15802
|
+
archivedKeysCollection: z.string().nullable(),
|
|
15803
|
+
/**
|
|
15804
|
+
* `system-settings` keys whose marker the reset cleared — the cluster
|
|
15805
|
+
* secret's: a marker left pointing at an archived value would refuse every
|
|
15806
|
+
* agent forever (D797).
|
|
15807
|
+
*/
|
|
15808
|
+
clearedSettingsKeys: z.array(z.string()).readonly(),
|
|
15809
|
+
/** When an operator dismissed the notice on the Secrets page; `null` until then. */
|
|
15810
|
+
acknowledgedAt: z.number().nullable()
|
|
15811
|
+
});
|
|
14987
15812
|
var KeychainStatusSchema = z.object({
|
|
14988
15813
|
keychain: KeychainStateSchema,
|
|
14989
15814
|
/** The source of the master key in use; `null` while locked. */
|
|
@@ -15001,7 +15826,9 @@ var KeychainStatusSchema = z.object({
|
|
|
15001
15826
|
/** Wrapped copies of the data key under a previous master key, kept until purged. */
|
|
15002
15827
|
retiredWraps: z.number().int(),
|
|
15003
15828
|
recoveryKeyDownloadedAt: z.number().nullable(),
|
|
15004
|
-
secretCount: z.number().int()
|
|
15829
|
+
secretCount: z.number().int(),
|
|
15830
|
+
/** The most recent reset, acknowledged or not; `null` when the keychain was never reset. */
|
|
15831
|
+
lastReset: KeychainResetRecordSchema.nullable()
|
|
15005
15832
|
});
|
|
15006
15833
|
/**
|
|
15007
15834
|
* Whether a stored value is still pointed at by its owner's settings.
|
|
@@ -15023,13 +15850,14 @@ var KeychainAuditActionSchema = z.enum([
|
|
|
15023
15850
|
"download-recovery-key",
|
|
15024
15851
|
"delete-orphans",
|
|
15025
15852
|
"reset",
|
|
15853
|
+
"acknowledge-reset",
|
|
15026
15854
|
"initialize"
|
|
15027
15855
|
]);
|
|
15028
15856
|
var KeychainAuditEntrySchema = z.object({
|
|
15029
15857
|
id: z.string(),
|
|
15030
15858
|
at: z.number(),
|
|
15031
15859
|
action: KeychainAuditActionSchema,
|
|
15032
|
-
/** `addon:<id>` | `user:<id>` | `system`. */
|
|
15860
|
+
/** `addon:<id>` | `user:<id>` | `system` | `trigger-file`. */
|
|
15033
15861
|
actor: z.string(),
|
|
15034
15862
|
ref: z.string().nullable(),
|
|
15035
15863
|
detail: z.string().nullable()
|
|
@@ -15139,6 +15967,21 @@ var keychainAdminCapability = {
|
|
|
15139
15967
|
kind: "mutation",
|
|
15140
15968
|
auth: "admin",
|
|
15141
15969
|
caller: "required"
|
|
15970
|
+
}),
|
|
15971
|
+
/**
|
|
15972
|
+
* What the last reset left to re-enter: the archived refs (owner, scope,
|
|
15973
|
+
* field — never a value) that hold no value again yet. Process-held refs
|
|
15974
|
+
* the hub regenerates itself (the session signing key) are not listed.
|
|
15975
|
+
*/
|
|
15976
|
+
resetReport: method(z.object({}), z.object({
|
|
15977
|
+
reset: KeychainResetRecordSchema.nullable(),
|
|
15978
|
+
pendingReentry: z.array(KeychainSecretMetaSchema).readonly()
|
|
15979
|
+
}), { auth: "admin" }),
|
|
15980
|
+
/** Dismiss the "keychain was reset" notice. Audited. */
|
|
15981
|
+
acknowledgeReset: method(z.object({}), z.object({ acknowledged: z.boolean() }), {
|
|
15982
|
+
kind: "mutation",
|
|
15983
|
+
auth: "admin",
|
|
15984
|
+
caller: "required"
|
|
15142
15985
|
})
|
|
15143
15986
|
}
|
|
15144
15987
|
};
|
|
@@ -18395,8 +19238,8 @@ var PUBLIC_ENDPOINT_HTTP_REASON = "The public endpoint needs an https:// address
|
|
|
18395
19238
|
* May `baseUrl` carry `role`? The address check above, plus two role rules:
|
|
18396
19239
|
* the app link carries `notificationTaps` only (it is not a hub address), and
|
|
18397
19240
|
* a plain-http address carries `publicEndpoint` only when the operator
|
|
18398
|
-
* explicitly accepted it (`acceptInsecureHttp`) — a LAN-only password
|
|
18399
|
-
*
|
|
19241
|
+
* explicitly accepted it (`acceptInsecureHttp`) — a LAN-only password
|
|
19242
|
+
* sign-in works over http, a passkey or an OIDC callback does not.
|
|
18400
19243
|
*/
|
|
18401
19244
|
function checkRoleAddress(role, baseUrl, options = {}) {
|
|
18402
19245
|
if (isAppLinkBase(baseUrl)) return role === "notificationTaps" ? {
|
|
@@ -19426,7 +20269,7 @@ var logDestinationCapability = {
|
|
|
19426
20269
|
* `login-method` — collection cap through which auth addons contribute
|
|
19427
20270
|
* their pre-auth login surfaces to the login page. This is the SINGLE,
|
|
19428
20271
|
* generic mechanism that supersedes the dead `auth.listProviders` reader:
|
|
19429
|
-
* every auth addon (OIDC,
|
|
20272
|
+
* every auth addon (OIDC, WebAuthn/passkey) registers a
|
|
19430
20273
|
* `login-method` provider and the PUBLIC `auth.listLoginMethods`
|
|
19431
20274
|
* procedure aggregates them for the unauthenticated login page.
|
|
19432
20275
|
*
|
|
@@ -19434,8 +20277,7 @@ var logDestinationCapability = {
|
|
|
19434
20277
|
*
|
|
19435
20278
|
* - `redirect` — a declarative button. The login page renders a generic
|
|
19436
20279
|
* button that navigates to `startUrl` (an addon-owned HTTP route).
|
|
19437
|
-
* Covers OIDC (`/addon/auth-oidc/<id>/start`)
|
|
19438
|
-
* ZERO shell-side JS. A future SSO addon plugs in the same way — the
|
|
20280
|
+
* Covers OIDC (`/addon/auth-oidc/<id>/start`) with ZERO shell-side JS. A future SSO addon plugs in the same way — the
|
|
19439
20281
|
* login page needs NO change.
|
|
19440
20282
|
*
|
|
19441
20283
|
* - `widget` — a Module-Federation widget the login page mounts (via
|
|
@@ -19454,8 +20296,8 @@ var logDestinationCapability = {
|
|
|
19454
20296
|
* decision.
|
|
19455
20297
|
*
|
|
19456
20298
|
* Every contribution carries a `stage`:
|
|
19457
|
-
* - `primary` — shown on the first credentials screen (OIDC
|
|
19458
|
-
*
|
|
20299
|
+
* - `primary` — shown on the first credentials screen (OIDC
|
|
20300
|
+
* buttons; a usernameless passkey).
|
|
19459
20301
|
* - `second-factor` — shown AFTER the password leg, gated on the
|
|
19460
20302
|
* returned `factors` (passkey-as-2FA today).
|
|
19461
20303
|
*
|
|
@@ -19467,7 +20309,7 @@ var logDestinationCapability = {
|
|
|
19467
20309
|
var LoginStageEnum = z.enum(["primary", "second-factor"]);
|
|
19468
20310
|
/**
|
|
19469
20311
|
* A declarative redirect button — the login page navigates to `startUrl`.
|
|
19470
|
-
* OIDC
|
|
20312
|
+
* OIDC contributes this; a future SSO addon does too.
|
|
19471
20313
|
*/
|
|
19472
20314
|
var RedirectLoginMethodSchema = z.object({
|
|
19473
20315
|
kind: z.literal("redirect"),
|
|
@@ -19484,7 +20326,7 @@ var RedirectLoginMethodSchema = z.object({
|
|
|
19484
20326
|
* The provider names an external identity a signed-in user can link to
|
|
19485
20327
|
* their own account (D782): `auth.listLinkableProviders` /
|
|
19486
20328
|
* `beginExternalLink` offer exactly the redirect methods that set this,
|
|
19487
|
-
* whatever their id. Absent / false — a login button only
|
|
20329
|
+
* whatever their id. Absent / false — a login button only.
|
|
19488
20330
|
*/
|
|
19489
20331
|
linkable: z.boolean().optional()
|
|
19490
20332
|
});
|
|
@@ -23755,8 +24597,7 @@ var notificationRulesCapability = {
|
|
|
23755
24597
|
* `super_admin` — collapsed onto the single boolean because every
|
|
23756
24598
|
* non-admin caller is governed by the same scope-check rules; the
|
|
23757
24599
|
* distinction was only ever used to route role-specific procedures
|
|
23758
|
-
* which we no longer have.
|
|
23759
|
-
* `createServiceToken`; CLI / webhook integrations are `cst_*` tokens
|
|
24600
|
+
* which we no longer have. CLI / webhook integrations are `cst_*` tokens
|
|
23760
24601
|
* with `isAdmin: false` + an explicit scope list.
|
|
23761
24602
|
*/
|
|
23762
24603
|
/** Per-method access flavour. Materialised on every cap method via codegen. */
|
|
@@ -27037,6 +27878,26 @@ var pipelineAnalyticsCapability = {
|
|
|
27037
27878
|
auth: "admin",
|
|
27038
27879
|
timeoutMs: 60 * 6e4
|
|
27039
27880
|
}),
|
|
27881
|
+
/**
|
|
27882
|
+
* OFFER the annotated archive instead of pushing it (D805): the same tar
|
|
27883
|
+
* `writeRetrainArchive` streams, behind a one-shot `ctx.peerBytes` ticket
|
|
27884
|
+
* the caller dials at once (same host). For a consumer that cannot expose
|
|
27885
|
+
* an upload URL — a training provider streaming straight into its cloud.
|
|
27886
|
+
* The answer is the plan; the bytes and their sha256 are the consumer's
|
|
27887
|
+
* to measure as they arrive.
|
|
27888
|
+
*/
|
|
27889
|
+
offerRetrainArchive: method(z.object({
|
|
27890
|
+
filter: RetrainExportFilterSchema,
|
|
27891
|
+
layout: RetrainExportLayoutSchema
|
|
27892
|
+
}), z.object({
|
|
27893
|
+
ticket: PeerBytesTicketSchema,
|
|
27894
|
+
frames: z.number().int(),
|
|
27895
|
+
annotations: z.number().int()
|
|
27896
|
+
}), {
|
|
27897
|
+
kind: "mutation",
|
|
27898
|
+
auth: "admin",
|
|
27899
|
+
timeoutMs: 12e4
|
|
27900
|
+
}),
|
|
27040
27901
|
/** The counts a selection would freeze — class × camera × day/night,
|
|
27041
27902
|
* completeness — without freezing anything. Index reads only. */
|
|
27042
27903
|
previewRetrainDataset: method(z.object({
|
|
@@ -31783,8 +32644,8 @@ version: z.string().optional() }), ServerUpdateActionResultSchema, {
|
|
|
31783
32644
|
* `smtp-provider` — pluggable email delivery surface.
|
|
31784
32645
|
*
|
|
31785
32646
|
* Collection cap: a deployment may install multiple SMTP relays (e.g.
|
|
31786
|
-
* one for transactional / one for notifications). Consumers (
|
|
31787
|
-
*
|
|
32647
|
+
* one for transactional / one for notifications). Consumers (notifier
|
|
32648
|
+
* addons, …) walk the collection and pick the first
|
|
31788
32649
|
* ready provider OR target a specific addonId.
|
|
31789
32650
|
*
|
|
31790
32651
|
* The shape stays small on purpose — `sendEmail` is enough for the
|
|
@@ -32080,7 +32941,7 @@ targets: z.array(z.object({
|
|
|
32080
32941
|
};
|
|
32081
32942
|
/**
|
|
32082
32943
|
* `sso-bridge` — internal hub-only cap that lets SSO-style auth
|
|
32083
|
-
* providers (OIDC, SAML,
|
|
32944
|
+
* providers (OIDC, SAML, …) mint an HMAC-signed token
|
|
32084
32945
|
* carrying the post-callback claims before redirecting back to
|
|
32085
32946
|
* `/api/auth/sso/finish`.
|
|
32086
32947
|
*
|
|
@@ -32143,7 +33004,14 @@ var SsoBridgeClaimsSchema = z.object({
|
|
|
32143
33004
|
* Verified `iss` of the id_token behind an OIDC login bridge (D782). A
|
|
32144
33005
|
* subject is unique only per issuer, so a link is bound to it.
|
|
32145
33006
|
*/
|
|
32146
|
-
issuer: z.string().optional()
|
|
33007
|
+
issuer: z.string().optional(),
|
|
33008
|
+
/**
|
|
33009
|
+
* SHA-256 (hex) of the `camstack_sso_state` cookie the IdP `/start` set in
|
|
33010
|
+
* the browser that began this sign-in. `/api/auth/sso/finish` requires the
|
|
33011
|
+
* request to present the matching cookie — a login bridge is useless in
|
|
33012
|
+
* any other browser.
|
|
33013
|
+
*/
|
|
33014
|
+
browserBinding: z.string().optional()
|
|
32147
33015
|
});
|
|
32148
33016
|
var ssoBridgeCapability = {
|
|
32149
33017
|
name: "sso-bridge",
|
|
@@ -32161,7 +33029,7 @@ var ssoBridgeCapability = {
|
|
|
32161
33029
|
* `/api/auth/sso/finish` to become admin — a full privilege escalation
|
|
32162
33030
|
* through the exact surface this cap exists to close (see the module
|
|
32163
33031
|
* docblock). Every real caller (`oauth-grants.ts`, `main.ts`
|
|
32164
|
-
* registration, the OIDC
|
|
33032
|
+
* registration, the OIDC addon) reaches this through
|
|
32165
33033
|
* `ctx.api` / the in-process registry — never through tRPC — so
|
|
32166
33034
|
* `auth: 'admin'` costs those callers nothing.
|
|
32167
33035
|
*/
|
|
@@ -46957,16 +47825,6 @@ var ApiKeySummarySchema = z.object({
|
|
|
46957
47825
|
createdAt: z.number(),
|
|
46958
47826
|
lastUsedAt: z.number().optional()
|
|
46959
47827
|
});
|
|
46960
|
-
var CreateApiKeyInputSchema = z.object({
|
|
46961
|
-
label: z.string(),
|
|
46962
|
-
isAdmin: z.boolean().default(false),
|
|
46963
|
-
allowedProviders: z.union([z.array(z.string()), z.literal("*")]).optional(),
|
|
46964
|
-
allowedDevices: z.record(z.string(), z.union([z.array(z.string()), z.literal("*")])).optional()
|
|
46965
|
-
});
|
|
46966
|
-
var CreateApiKeyResultSchema = z.object({
|
|
46967
|
-
token: z.string(),
|
|
46968
|
-
record: ApiKeySummarySchema
|
|
46969
|
-
});
|
|
46970
47828
|
var ScopedTokenSummarySchema = z.object({
|
|
46971
47829
|
id: z.string(),
|
|
46972
47830
|
userId: z.string(),
|
|
@@ -47078,20 +47936,11 @@ var userManagementCapability = {
|
|
|
47078
47936
|
access: "view"
|
|
47079
47937
|
}),
|
|
47080
47938
|
listApiKeys: method(z.void(), z.array(ApiKeySummarySchema), { auth: "admin" }),
|
|
47081
|
-
createApiKey: method(CreateApiKeyInputSchema, CreateApiKeyResultSchema, {
|
|
47082
|
-
kind: "mutation",
|
|
47083
|
-
auth: "admin",
|
|
47084
|
-
access: "create"
|
|
47085
|
-
}),
|
|
47086
47939
|
revokeApiKey: method(z.object({ id: z.string() }), z.object({ success: z.literal(true) }), {
|
|
47087
47940
|
kind: "mutation",
|
|
47088
47941
|
auth: "admin",
|
|
47089
47942
|
access: "delete"
|
|
47090
47943
|
}),
|
|
47091
|
-
validateApiKey: method(z.object({ token: z.string() }), ApiKeySummarySchema.nullable(), {
|
|
47092
|
-
kind: "mutation",
|
|
47093
|
-
access: "view"
|
|
47094
|
-
}),
|
|
47095
47944
|
createScopedToken: method(CreateScopedTokenInputSchema, CreateScopedTokenResultSchema, {
|
|
47096
47945
|
kind: "mutation",
|
|
47097
47946
|
auth: "admin",
|
|
@@ -47181,20 +48030,6 @@ var userManagementCapability = {
|
|
|
47181
48030
|
userId: z.string(),
|
|
47182
48031
|
issuer: z.string()
|
|
47183
48032
|
}).nullable(), { auth: "admin" }),
|
|
47184
|
-
/**
|
|
47185
|
-
* Link `(provider, subject)` to `userId`. Idempotent for the same user;
|
|
47186
|
-
* REFUSES (throws) an identity already linked to another user, and a
|
|
47187
|
-
* user that does not exist.
|
|
47188
|
-
*/
|
|
47189
|
-
linkExternalIdentity: method(ExternalIdentityKeySchema.extend({
|
|
47190
|
-
userId: z.string(),
|
|
47191
|
-
issuer: z.string().min(1),
|
|
47192
|
-
label: z.string().optional()
|
|
47193
|
-
}), z.object({ success: z.literal(true) }), {
|
|
47194
|
-
kind: "mutation",
|
|
47195
|
-
auth: "admin",
|
|
47196
|
-
access: "create"
|
|
47197
|
-
}),
|
|
47198
48033
|
/** Remove one link of `userId`. `success: false` when it was not linked to that user. */
|
|
47199
48034
|
unlinkExternalIdentity: method(ExternalIdentityKeySchema.extend({ userId: z.string() }), z.object({ success: z.boolean() }), {
|
|
47200
48035
|
kind: "mutation",
|
|
@@ -47923,6 +48758,531 @@ var zoneRulesCapability = {
|
|
|
47923
48758
|
*/
|
|
47924
48759
|
durability: "restored"
|
|
47925
48760
|
};
|
|
48761
|
+
/**
|
|
48762
|
+
* `training-provider` — ONE cloud that can run the CamStack trainer.
|
|
48763
|
+
*
|
|
48764
|
+
* A thin, mockable translation layer: it knows how to run a container with a
|
|
48765
|
+
* GPU, move a dataset in, read the job's stdout and hand artefacts back. It
|
|
48766
|
+
* knows nothing about datasets, splits, floors, cost policy or Model Studio —
|
|
48767
|
+
* those are the `training` orchestrator's (D805). Every rule written there
|
|
48768
|
+
* once is what makes a second provider (RunPod, a local NVIDIA node) a new
|
|
48769
|
+
* manifest entry and nothing else.
|
|
48770
|
+
*
|
|
48771
|
+
* Collection, routed by the `addonId` in every single-row input (the `llm` /
|
|
48772
|
+
* `notification-output` posture); `describe` concat-fans across providers.
|
|
48773
|
+
* Every method is `admin`: these spend money.
|
|
48774
|
+
*
|
|
48775
|
+
* Every cloud object a provider creates is tagged with the orchestrator's
|
|
48776
|
+
* `jobKey`, so a retried submit finds the job it already started instead of
|
|
48777
|
+
* starting a second GPU, and `listJobs` can find orphans.
|
|
48778
|
+
*
|
|
48779
|
+
* Bytes never ride a cap field: the dataset arrives as a `ctx.peerBytes`
|
|
48780
|
+
* ticket the producer (pipeline-analytics) minted, and artefacts leave as a
|
|
48781
|
+
* ticket the provider mints (D613).
|
|
48782
|
+
*/
|
|
48783
|
+
var TrainingGpuOptionSchema = z.object({
|
|
48784
|
+
id: z.string(),
|
|
48785
|
+
label: z.string(),
|
|
48786
|
+
/** List price, USD per GPU-hour, before any region multiplier. */
|
|
48787
|
+
usdPerHour: z.number().nonnegative(),
|
|
48788
|
+
vramGb: z.number().nonnegative()
|
|
48789
|
+
});
|
|
48790
|
+
var TrainingRegionOptionSchema = z.object({
|
|
48791
|
+
/** `''` = any region (the provider's choice). */
|
|
48792
|
+
id: z.string(),
|
|
48793
|
+
label: z.string(),
|
|
48794
|
+
priceMultiplier: z.number().positive()
|
|
48795
|
+
});
|
|
48796
|
+
/**
|
|
48797
|
+
* The provider's per-account settings, as the orchestrator reads them. They
|
|
48798
|
+
* live in the PROVIDER addon's settings (operator decision 2026-10-08: they
|
|
48799
|
+
* depend on the user's account), never hard-coded in the orchestrator.
|
|
48800
|
+
*/
|
|
48801
|
+
var TrainingProviderDefaultsSchema = z.object({
|
|
48802
|
+
gpuType: z.string(),
|
|
48803
|
+
region: z.string(),
|
|
48804
|
+
costCapUsd: z.number().nonnegative(),
|
|
48805
|
+
maxRuntimeSec: z.number().int().positive(),
|
|
48806
|
+
/** Hours a FAILED job's cloud dataset is kept for debugging; 0 = delete at once. */
|
|
48807
|
+
failedDatasetGraceHours: z.number().nonnegative()
|
|
48808
|
+
});
|
|
48809
|
+
var TrainingProviderDescriptorSchema = z.object({
|
|
48810
|
+
addonId: z.string(),
|
|
48811
|
+
/** `modal`, `runpod`, `fake`, … */
|
|
48812
|
+
kind: z.string(),
|
|
48813
|
+
label: z.string(),
|
|
48814
|
+
gpuTypes: z.array(TrainingGpuOptionSchema).readonly(),
|
|
48815
|
+
regions: z.array(TrainingRegionOptionSchema).readonly(),
|
|
48816
|
+
maxDatasetBytes: z.number().int().positive(),
|
|
48817
|
+
maxRuntimeSec: z.number().int().positive(),
|
|
48818
|
+
freeTierNote: z.string().optional(),
|
|
48819
|
+
/** Where the data goes, in one sentence the confirm dialog repeats. */
|
|
48820
|
+
dataResidencyNote: z.string(),
|
|
48821
|
+
/** Credentials are present — never the values. */
|
|
48822
|
+
configured: z.boolean(),
|
|
48823
|
+
defaults: TrainingProviderDefaultsSchema
|
|
48824
|
+
});
|
|
48825
|
+
var TrainingCredentialCheckSchema = z.object({
|
|
48826
|
+
ok: z.boolean(),
|
|
48827
|
+
/** Workspace / account name, when the provider says. */
|
|
48828
|
+
account: z.string().optional(),
|
|
48829
|
+
reason: z.enum([
|
|
48830
|
+
"missing",
|
|
48831
|
+
"invalid",
|
|
48832
|
+
"forbidden",
|
|
48833
|
+
"network",
|
|
48834
|
+
"quota"
|
|
48835
|
+
]).optional(),
|
|
48836
|
+
detail: z.string().optional()
|
|
48837
|
+
});
|
|
48838
|
+
var ProviderJobStateSchema = z.enum([
|
|
48839
|
+
"pending",
|
|
48840
|
+
"starting",
|
|
48841
|
+
"running",
|
|
48842
|
+
"succeeded",
|
|
48843
|
+
"failed",
|
|
48844
|
+
"cancelled",
|
|
48845
|
+
"lost"
|
|
48846
|
+
]);
|
|
48847
|
+
var ProviderJobStatusSchema = z.object({
|
|
48848
|
+
jobKey: z.string(),
|
|
48849
|
+
providerJobId: z.string().optional(),
|
|
48850
|
+
state: ProviderJobStateSchema,
|
|
48851
|
+
startedAt: z.number().optional(),
|
|
48852
|
+
endedAt: z.number().optional(),
|
|
48853
|
+
exitCode: z.number().int().optional(),
|
|
48854
|
+
failureReason: z.string().optional(),
|
|
48855
|
+
gpuType: z.string(),
|
|
48856
|
+
region: z.string().optional(),
|
|
48857
|
+
billedSeconds: z.number().nonnegative(),
|
|
48858
|
+
costUsd: z.number().nonnegative(),
|
|
48859
|
+
/** True when `costUsd` is runtime × list price, not a billing answer. */
|
|
48860
|
+
costIsEstimate: z.boolean(),
|
|
48861
|
+
lastFetchedAt: z.number()
|
|
48862
|
+
});
|
|
48863
|
+
/**
|
|
48864
|
+
* The trainer the job runs, by SOURCE revision: the provider turns it into an
|
|
48865
|
+
* image its own way (Modal builds the published Dockerfile at that revision;
|
|
48866
|
+
* a registry-pull provider maps it to a tag). Recorded on the job and echoed
|
|
48867
|
+
* in `result.json`.
|
|
48868
|
+
*/
|
|
48869
|
+
var TrainerSourceRefSchema = z.object({
|
|
48870
|
+
/** Hugging Face repo id, e.g. `camstack/camstack-trainer`. */
|
|
48871
|
+
repo: z.string().min(1),
|
|
48872
|
+
/** A commit sha (never a branch) — the job must be reproducible. */
|
|
48873
|
+
revision: z.string().regex(/^[0-9a-f]{7,64}$/)
|
|
48874
|
+
});
|
|
48875
|
+
var JobRef = {
|
|
48876
|
+
addonId: z.string(),
|
|
48877
|
+
jobKey: z.string().uuid()
|
|
48878
|
+
};
|
|
48879
|
+
var TrainingLogLineSchema = z.object({
|
|
48880
|
+
ts: z.number(),
|
|
48881
|
+
text: z.string()
|
|
48882
|
+
});
|
|
48883
|
+
var trainingProviderCapability = {
|
|
48884
|
+
name: "training-provider",
|
|
48885
|
+
scope: "system",
|
|
48886
|
+
mode: "collection",
|
|
48887
|
+
internal: true,
|
|
48888
|
+
methods: {
|
|
48889
|
+
/** Every provider's offer and settings. Concat-fans across providers. */
|
|
48890
|
+
describe: method(z.object({}), z.array(TrainingProviderDescriptorSchema).readonly(), { auth: "admin" }),
|
|
48891
|
+
/** Call the provider with the stored credentials. Never returns them. */
|
|
48892
|
+
validateCredentials: method(z.object({ addonId: z.string() }), TrainingCredentialCheckSchema, {
|
|
48893
|
+
kind: "mutation",
|
|
48894
|
+
auth: "admin",
|
|
48895
|
+
timeoutMs: 3e4
|
|
48896
|
+
}),
|
|
48897
|
+
/** USD range for a run of `gpuSeconds` on `gpuType` in `region`. */
|
|
48898
|
+
estimateJob: method(z.object({
|
|
48899
|
+
addonId: z.string(),
|
|
48900
|
+
gpuType: z.string(),
|
|
48901
|
+
region: z.string(),
|
|
48902
|
+
datasetBytes: z.number().int().nonnegative(),
|
|
48903
|
+
gpuSecondsLow: z.number().nonnegative(),
|
|
48904
|
+
gpuSecondsHigh: z.number().nonnegative()
|
|
48905
|
+
}), z.object({
|
|
48906
|
+
usdLow: z.number().nonnegative(),
|
|
48907
|
+
usdHigh: z.number().nonnegative(),
|
|
48908
|
+
notes: z.array(z.string()).readonly()
|
|
48909
|
+
}), { auth: "admin" }),
|
|
48910
|
+
/**
|
|
48911
|
+
* Take the dataset archive behind `ticket` (minted by pipeline-analytics,
|
|
48912
|
+
* same host) and land it as the job's dataset in the cloud, streamed —
|
|
48913
|
+
* nothing is staged on the hub's disk. Idempotent per `jobKey`: a second
|
|
48914
|
+
* call replaces a partial upload. Answers what actually landed.
|
|
48915
|
+
*/
|
|
48916
|
+
uploadDataset: method(z.object({
|
|
48917
|
+
...JobRef,
|
|
48918
|
+
ticket: PeerBytesTicketSchema
|
|
48919
|
+
}), z.object({
|
|
48920
|
+
bytes: z.number().int().nonnegative(),
|
|
48921
|
+
sha256: z.string()
|
|
48922
|
+
}), {
|
|
48923
|
+
kind: "mutation",
|
|
48924
|
+
auth: "admin",
|
|
48925
|
+
timeoutMs: 120 * 6e4
|
|
48926
|
+
}),
|
|
48927
|
+
/**
|
|
48928
|
+
* Start the trainer on a GPU. Looks the job up by `jobKey` first, so a
|
|
48929
|
+
* retried submit never starts a second GPU. `jobSpecJson` is written next
|
|
48930
|
+
* to the dataset as `job.json`; `maxRuntimeSec` is enforced by the
|
|
48931
|
+
* provider (a hard timeout), not by the hub.
|
|
48932
|
+
*/
|
|
48933
|
+
submitJob: method(z.object({
|
|
48934
|
+
...JobRef,
|
|
48935
|
+
trainer: TrainerSourceRefSchema,
|
|
48936
|
+
jobSpecJson: z.string().min(2).max(64e3),
|
|
48937
|
+
gpuType: z.string(),
|
|
48938
|
+
region: z.string(),
|
|
48939
|
+
maxRuntimeSec: z.number().int().positive()
|
|
48940
|
+
}), ProviderJobStatusSchema, {
|
|
48941
|
+
kind: "mutation",
|
|
48942
|
+
auth: "admin",
|
|
48943
|
+
timeoutMs: 30 * 6e4
|
|
48944
|
+
}),
|
|
48945
|
+
getJob: method(z.object(JobRef), ProviderJobStatusSchema, {
|
|
48946
|
+
auth: "admin",
|
|
48947
|
+
timeoutMs: 6e4
|
|
48948
|
+
}),
|
|
48949
|
+
/** Stdout/stderr since `cursor` (opaque), oldest first, at most `max` lines. */
|
|
48950
|
+
listJobEvents: method(z.object({
|
|
48951
|
+
...JobRef,
|
|
48952
|
+
cursor: z.string().optional(),
|
|
48953
|
+
max: z.number().int().min(1).max(500)
|
|
48954
|
+
}), z.object({
|
|
48955
|
+
lines: z.array(TrainingLogLineSchema).readonly(),
|
|
48956
|
+
cursor: z.string()
|
|
48957
|
+
}), {
|
|
48958
|
+
auth: "admin",
|
|
48959
|
+
timeoutMs: 6e4
|
|
48960
|
+
}),
|
|
48961
|
+
/** Stop the job. Idempotent: an unknown or finished job is a no-op. */
|
|
48962
|
+
cancelJob: method(z.object(JobRef), z.object({ ok: z.literal(true) }), {
|
|
48963
|
+
kind: "mutation",
|
|
48964
|
+
auth: "admin",
|
|
48965
|
+
timeoutMs: 6e4
|
|
48966
|
+
}),
|
|
48967
|
+
/** The job's `result.json`, as text (small, JSON). `found: false` before it exists. */
|
|
48968
|
+
readResult: method(z.object(JobRef), z.object({
|
|
48969
|
+
found: z.boolean(),
|
|
48970
|
+
text: z.string().optional()
|
|
48971
|
+
}), {
|
|
48972
|
+
kind: "mutation",
|
|
48973
|
+
auth: "admin",
|
|
48974
|
+
timeoutMs: 5 * 6e4
|
|
48975
|
+
}),
|
|
48976
|
+
/**
|
|
48977
|
+
* Offer one output file of the job (`bundle.tar`) to the caller as a
|
|
48978
|
+
* one-shot `ctx.peerBytes` ticket, streamed from the cloud — the provider
|
|
48979
|
+
* stages nothing on disk. The caller dials it at once.
|
|
48980
|
+
*/
|
|
48981
|
+
offerArtifact: method(z.object({
|
|
48982
|
+
...JobRef,
|
|
48983
|
+
path: z.enum(["bundle.tar"])
|
|
48984
|
+
}), z.object({ ticket: PeerBytesTicketSchema }), {
|
|
48985
|
+
kind: "mutation",
|
|
48986
|
+
auth: "admin",
|
|
48987
|
+
timeoutMs: 5 * 6e4
|
|
48988
|
+
}),
|
|
48989
|
+
/** Delete every cloud object tagged with `jobKey` (the dataset first). Idempotent. */
|
|
48990
|
+
cleanupJob: method(z.object(JobRef), z.object({ deleted: z.array(z.string()).readonly() }), {
|
|
48991
|
+
kind: "mutation",
|
|
48992
|
+
auth: "admin",
|
|
48993
|
+
timeoutMs: 5 * 6e4
|
|
48994
|
+
}),
|
|
48995
|
+
/** Every job the provider can still see — the orphan sweep diffs it against our records. */
|
|
48996
|
+
listJobs: method(z.object({ addonId: z.string() }), z.array(ProviderJobStatusSchema).readonly(), {
|
|
48997
|
+
auth: "admin",
|
|
48998
|
+
timeoutMs: 2 * 6e4
|
|
48999
|
+
})
|
|
49000
|
+
}
|
|
49001
|
+
};
|
|
49002
|
+
/**
|
|
49003
|
+
* `training` — the cloud-training orchestrator (D805). Hub-only singleton,
|
|
49004
|
+
* provided by `addon-training`.
|
|
49005
|
+
*
|
|
49006
|
+
* It owns what every provider must agree on, written once:
|
|
49007
|
+
*
|
|
49008
|
+
* - **Dataset versions.** Taken over from pipeline-analytics (D796): the rows
|
|
49009
|
+
* live here (D808); pipeline-analytics still CUTS a version (it owns the corpus and
|
|
49010
|
+
* the export plan) and reaches this store through the five row methods
|
|
49011
|
+
* below, so the Retrain page and the export did not change.
|
|
49012
|
+
* - **Jobs.** A persisted state machine, a reconcile loop against the
|
|
49013
|
+
* provider, a cost guard, a daily orphan sweep and the retention rules.
|
|
49014
|
+
* - **The return flow.** Verified artefacts → Model Studio
|
|
49015
|
+
* (`custom-model-registry.importModelBundle`) → canary on cameras →
|
|
49016
|
+
* rollback. Nothing is ever registered automatically.
|
|
49017
|
+
*
|
|
49018
|
+
* Every method is `admin`.
|
|
49019
|
+
*/
|
|
49020
|
+
/** A version WITH its frame list — what the export and a job read. */
|
|
49021
|
+
var TrainingDatasetVersionRowSchema = RetrainDatasetVersionSchema.omit({ frameCount: true }).extend({ frameIds: z.array(z.string()).max(2e4).readonly() });
|
|
49022
|
+
/** A base model a detector job can fine-tune, and the baseline it is judged against. */
|
|
49023
|
+
var TrainingBaseModelSchema = z.object({
|
|
49024
|
+
modelId: z.string(),
|
|
49025
|
+
label: z.string(),
|
|
49026
|
+
weights: z.string(),
|
|
49027
|
+
imgsz: z.number().int(),
|
|
49028
|
+
baselineOnnxUrl: z.string(),
|
|
49029
|
+
baselineFloors: z.record(z.string(), z.number()),
|
|
49030
|
+
/** The base the fleet runs most — the UI's default. */
|
|
49031
|
+
recommended: z.boolean()
|
|
49032
|
+
});
|
|
49033
|
+
var TrainingPresetSchema = z.enum([
|
|
49034
|
+
"quick",
|
|
49035
|
+
"standard",
|
|
49036
|
+
"thorough"
|
|
49037
|
+
]);
|
|
49038
|
+
var TrainingJobRequestSchema = z.object({
|
|
49039
|
+
datasetVersionId: z.string().min(1),
|
|
49040
|
+
/** The `training-provider` addon that runs it. */
|
|
49041
|
+
addonId: z.string().min(1),
|
|
49042
|
+
gpuType: z.string().min(1),
|
|
49043
|
+
region: z.string(),
|
|
49044
|
+
baseModelId: z.string().min(1),
|
|
49045
|
+
preset: TrainingPresetSchema,
|
|
49046
|
+
/** Freeze the backbone's first N layers; `null` = the preset's choice. */
|
|
49047
|
+
freeze: z.number().int().nonnegative().nullable(),
|
|
49048
|
+
/** Cameras held out whole for evaluation; `null` = the orchestrator's default. */
|
|
49049
|
+
holdoutCameras: z.array(z.number().int()).nullable()
|
|
49050
|
+
});
|
|
49051
|
+
var TrainingSplitPlanSchema = z.object({
|
|
49052
|
+
holdoutCameras: z.array(z.number().int()).readonly(),
|
|
49053
|
+
/** Used when no camera is held out: the last N days of every camera. */
|
|
49054
|
+
holdoutLastDays: z.number().int().nonnegative(),
|
|
49055
|
+
/** Holdout subjects per class (camera holdout only; `null` = known only after the split). */
|
|
49056
|
+
holdoutSupport: z.record(z.string(), z.number().int()).nullable(),
|
|
49057
|
+
trainSubjects: z.number().int()
|
|
49058
|
+
});
|
|
49059
|
+
var TrainingRefusalReasonSchema = z.enum([
|
|
49060
|
+
"unknown-dataset",
|
|
49061
|
+
"unknown-provider",
|
|
49062
|
+
"provider-not-configured",
|
|
49063
|
+
"unknown-base-model",
|
|
49064
|
+
"unknown-gpu",
|
|
49065
|
+
"dataset-too-large",
|
|
49066
|
+
"no-training-subjects",
|
|
49067
|
+
"job-already-running"
|
|
49068
|
+
]);
|
|
49069
|
+
var TrainingJobPlanSchema = z.object({
|
|
49070
|
+
refusal: z.object({
|
|
49071
|
+
reason: TrainingRefusalReasonSchema,
|
|
49072
|
+
detail: z.string()
|
|
49073
|
+
}).nullable(),
|
|
49074
|
+
warnings: z.array(z.string()).readonly(),
|
|
49075
|
+
split: TrainingSplitPlanSchema.nullable(),
|
|
49076
|
+
estimate: z.object({
|
|
49077
|
+
datasetBytes: z.number().int(),
|
|
49078
|
+
uploadMinutesAt20Mbps: z.number(),
|
|
49079
|
+
gpuSecondsLow: z.number(),
|
|
49080
|
+
gpuSecondsHigh: z.number(),
|
|
49081
|
+
usdLow: z.number(),
|
|
49082
|
+
usdHigh: z.number(),
|
|
49083
|
+
notes: z.array(z.string()).readonly()
|
|
49084
|
+
}).nullable(),
|
|
49085
|
+
/** Pre-filled from the provider's settings; the operator may lower or raise them. */
|
|
49086
|
+
defaults: z.object({
|
|
49087
|
+
maxCostUsd: z.number(),
|
|
49088
|
+
maxRuntimeSec: z.number().int()
|
|
49089
|
+
}).nullable(),
|
|
49090
|
+
/** The sentence the confirm dialog repeats: where the frames go and when they are deleted. */
|
|
49091
|
+
dataNotice: z.string()
|
|
49092
|
+
});
|
|
49093
|
+
/**
|
|
49094
|
+
* Orchestrator-owned states, persisted. `uploading` covers exporting the
|
|
49095
|
+
* archive (it is streamed, never staged). Terminal: `failed`, `cancelled`,
|
|
49096
|
+
* `lost`, `expired`, `discarded`. `registered` and `canary` are where a
|
|
49097
|
+
* result lives after the operator acted on it.
|
|
49098
|
+
*/
|
|
49099
|
+
var TrainingJobStateSchema = z.enum([
|
|
49100
|
+
"uploading",
|
|
49101
|
+
"submitted",
|
|
49102
|
+
"starting",
|
|
49103
|
+
"running",
|
|
49104
|
+
"collecting",
|
|
49105
|
+
"ready",
|
|
49106
|
+
"registered",
|
|
49107
|
+
"canary",
|
|
49108
|
+
"failed",
|
|
49109
|
+
"cancelling",
|
|
49110
|
+
"cancelled",
|
|
49111
|
+
"lost",
|
|
49112
|
+
"expired",
|
|
49113
|
+
"discarded"
|
|
49114
|
+
]);
|
|
49115
|
+
var TrainingFailureReasonSchema = z.enum([
|
|
49116
|
+
"cost-cap",
|
|
49117
|
+
"runtime-cap",
|
|
49118
|
+
"upload-failed",
|
|
49119
|
+
"submit-failed",
|
|
49120
|
+
"trainer-failed",
|
|
49121
|
+
"result-invalid",
|
|
49122
|
+
"checksum-mismatch",
|
|
49123
|
+
"fetch-failed",
|
|
49124
|
+
"provider-error",
|
|
49125
|
+
"hub-restart-during-upload"
|
|
49126
|
+
]);
|
|
49127
|
+
var TrainingProgressPointSchema = z.object({
|
|
49128
|
+
epoch: z.number().int(),
|
|
49129
|
+
map50: z.number().nullable(),
|
|
49130
|
+
map50_95: z.number().nullable()
|
|
49131
|
+
});
|
|
49132
|
+
var TrainingCanarySchema = z.object({
|
|
49133
|
+
deviceIds: z.array(z.number().int()).readonly(),
|
|
49134
|
+
since: z.number(),
|
|
49135
|
+
/** Every override the canary replaced, exactly, so rollback restores it. */
|
|
49136
|
+
previous: z.array(z.object({
|
|
49137
|
+
deviceId: z.number().int(),
|
|
49138
|
+
nodeId: z.string(),
|
|
49139
|
+
deviceKey: z.string(),
|
|
49140
|
+
patch: z.object({
|
|
49141
|
+
modelId: z.string().optional(),
|
|
49142
|
+
settings: z.record(z.string(), z.unknown()).readonly().optional()
|
|
49143
|
+
}).nullable()
|
|
49144
|
+
})).readonly()
|
|
49145
|
+
});
|
|
49146
|
+
var TrainingJobSchema = z.object({
|
|
49147
|
+
id: z.string(),
|
|
49148
|
+
idempotencyKey: z.string(),
|
|
49149
|
+
name: z.string(),
|
|
49150
|
+
createdAt: z.number(),
|
|
49151
|
+
updatedAt: z.number(),
|
|
49152
|
+
state: TrainingJobStateSchema,
|
|
49153
|
+
stage: TrainingStageSchema.nullable(),
|
|
49154
|
+
failure: z.object({
|
|
49155
|
+
reason: TrainingFailureReasonSchema,
|
|
49156
|
+
stage: z.string().nullable(),
|
|
49157
|
+
detail: z.string()
|
|
49158
|
+
}).nullable(),
|
|
49159
|
+
request: TrainingJobRequestSchema,
|
|
49160
|
+
maxCostUsd: z.number().nonnegative(),
|
|
49161
|
+
maxRuntimeSec: z.number().int().positive(),
|
|
49162
|
+
trainer: TrainerSourceRefSchema,
|
|
49163
|
+
datasetLayout: RetrainExportLayoutSchema,
|
|
49164
|
+
dataset: z.object({
|
|
49165
|
+
bytes: z.number().int(),
|
|
49166
|
+
sha256: z.string(),
|
|
49167
|
+
uploadedAt: z.number()
|
|
49168
|
+
}).nullable(),
|
|
49169
|
+
split: TrainingSplitPlanSchema,
|
|
49170
|
+
provider: z.object({
|
|
49171
|
+
state: ProviderJobStateSchema.nullable(),
|
|
49172
|
+
providerJobId: z.string().nullable(),
|
|
49173
|
+
startedAt: z.number().nullable(),
|
|
49174
|
+
endedAt: z.number().nullable(),
|
|
49175
|
+
billedSeconds: z.number(),
|
|
49176
|
+
costUsd: z.number(),
|
|
49177
|
+
costIsEstimate: z.boolean(),
|
|
49178
|
+
lastPolledAt: z.number().nullable()
|
|
49179
|
+
}),
|
|
49180
|
+
progress: z.object({
|
|
49181
|
+
epoch: z.number().int().nullable(),
|
|
49182
|
+
epochs: z.number().int().nullable(),
|
|
49183
|
+
etaSec: z.number().nullable(),
|
|
49184
|
+
series: z.array(TrainingProgressPointSchema).readonly()
|
|
49185
|
+
}),
|
|
49186
|
+
/** The last lines of the job's output (≤ 200), newest last. Never frame paths. */
|
|
49187
|
+
logTail: z.array(z.string()).readonly(),
|
|
49188
|
+
logCursor: z.string().nullable(),
|
|
49189
|
+
artifacts: z.object({
|
|
49190
|
+
bundleBytes: z.number().int(),
|
|
49191
|
+
bundleSha256: z.string(),
|
|
49192
|
+
fetchedAt: z.number(),
|
|
49193
|
+
/** When the staged artefacts are deleted from the hub. */
|
|
49194
|
+
expiresAt: z.number()
|
|
49195
|
+
}).nullable(),
|
|
49196
|
+
/** When the provider-side copy of the frames was deleted; null while it exists. */
|
|
49197
|
+
cloudDatasetDeletedAt: z.number().nullable(),
|
|
49198
|
+
/** A failed job's dataset is kept until then (the provider's grace), then deleted. */
|
|
49199
|
+
cloudCleanupDueAt: z.number().nullable(),
|
|
49200
|
+
/** Model ids registered from this job (base, and int8 when kept). */
|
|
49201
|
+
registeredModelIds: z.array(z.string()).readonly(),
|
|
49202
|
+
canary: TrainingCanarySchema.nullable()
|
|
49203
|
+
});
|
|
49204
|
+
var IdInput = z.object({ id: z.string().min(1) });
|
|
49205
|
+
var trainingCapability = {
|
|
49206
|
+
name: "training",
|
|
49207
|
+
scope: "system",
|
|
49208
|
+
mode: "singleton",
|
|
49209
|
+
mount: { kind: "hub-only" },
|
|
49210
|
+
methods: {
|
|
49211
|
+
/** Store a version once. `stored: false` = that id already exists (immutable). */
|
|
49212
|
+
putDatasetVersion: method(TrainingDatasetVersionRowSchema, z.object({ stored: z.boolean() }), {
|
|
49213
|
+
kind: "mutation",
|
|
49214
|
+
auth: "admin"
|
|
49215
|
+
}),
|
|
49216
|
+
getDatasetVersion: method(IdInput, TrainingDatasetVersionRowSchema.nullable(), { auth: "admin" }),
|
|
49217
|
+
/** Every version without its frame list, newest first. */
|
|
49218
|
+
listDatasetVersions: method(z.object({}), z.array(RetrainDatasetVersionSchema).readonly(), { auth: "admin" }),
|
|
49219
|
+
findDatasetVersionIdByName: method(z.object({ name: z.string() }), z.object({ id: z.string().nullable() }), { auth: "admin" }),
|
|
49220
|
+
removeDatasetVersion: method(IdInput, z.object({ removed: z.boolean() }), {
|
|
49221
|
+
kind: "mutation",
|
|
49222
|
+
auth: "admin"
|
|
49223
|
+
}),
|
|
49224
|
+
listBaseModels: method(z.object({}), z.array(TrainingBaseModelSchema).readonly(), { auth: "admin" }),
|
|
49225
|
+
/** Split, estimate, defaults and any named refusal — nothing is started. */
|
|
49226
|
+
planJob: method(TrainingJobRequestSchema, TrainingJobPlanSchema, {
|
|
49227
|
+
auth: "admin",
|
|
49228
|
+
timeoutMs: 6e4
|
|
49229
|
+
}),
|
|
49230
|
+
/**
|
|
49231
|
+
* Start a job. The same `idempotencyKey` returns the job it already
|
|
49232
|
+
* started. Answers at once (`uploading`); the upload and the submit run in
|
|
49233
|
+
* the background and the job record says how far they got.
|
|
49234
|
+
*/
|
|
49235
|
+
startJob: method(TrainingJobRequestSchema.extend({
|
|
49236
|
+
name: z.string().min(1).max(120),
|
|
49237
|
+
maxCostUsd: z.number().positive(),
|
|
49238
|
+
maxRuntimeSec: z.number().int().positive(),
|
|
49239
|
+
idempotencyKey: z.string().min(8).max(80)
|
|
49240
|
+
}), z.discriminatedUnion("kind", [z.object({
|
|
49241
|
+
kind: z.literal("started"),
|
|
49242
|
+
job: TrainingJobSchema
|
|
49243
|
+
}), z.object({
|
|
49244
|
+
kind: z.literal("refused"),
|
|
49245
|
+
reason: TrainingRefusalReasonSchema,
|
|
49246
|
+
detail: z.string()
|
|
49247
|
+
})]), {
|
|
49248
|
+
kind: "mutation",
|
|
49249
|
+
auth: "admin",
|
|
49250
|
+
timeoutMs: 12e4
|
|
49251
|
+
}),
|
|
49252
|
+
listJobs: method(z.object({}), z.array(TrainingJobSchema).readonly(), { auth: "admin" }),
|
|
49253
|
+
getJob: method(IdInput, TrainingJobSchema.nullable(), { auth: "admin" }),
|
|
49254
|
+
cancelJob: method(IdInput, TrainingJobSchema, {
|
|
49255
|
+
kind: "mutation",
|
|
49256
|
+
auth: "admin",
|
|
49257
|
+
timeoutMs: 12e4
|
|
49258
|
+
}),
|
|
49259
|
+
/** The verified `result.json` of a job in `ready` or later; null before. */
|
|
49260
|
+
getJobResult: method(IdInput, TrainingResultSchema.nullable(), { auth: "admin" }),
|
|
49261
|
+
/** Hand the verified bundle to Model Studio; optionally canary right after. */
|
|
49262
|
+
registerResult: method(IdInput.extend({ canaryDeviceIds: z.array(z.number().int()).readonly() }), TrainingJobSchema, {
|
|
49263
|
+
kind: "mutation",
|
|
49264
|
+
auth: "admin",
|
|
49265
|
+
timeoutMs: 30 * 6e4
|
|
49266
|
+
}),
|
|
49267
|
+
/** Pin the registered model on these cameras, remembering what it replaced. */
|
|
49268
|
+
canaryResult: method(IdInput.extend({ deviceIds: z.array(z.number().int()).min(1).readonly() }), TrainingJobSchema, {
|
|
49269
|
+
kind: "mutation",
|
|
49270
|
+
auth: "admin",
|
|
49271
|
+
timeoutMs: 12e4
|
|
49272
|
+
}),
|
|
49273
|
+
/** Put every canary camera back exactly as it was. */
|
|
49274
|
+
rollbackCanary: method(IdInput, TrainingJobSchema, {
|
|
49275
|
+
kind: "mutation",
|
|
49276
|
+
auth: "admin",
|
|
49277
|
+
timeoutMs: 12e4
|
|
49278
|
+
}),
|
|
49279
|
+
/** Delete the staged artefacts of an unregistered result. */
|
|
49280
|
+
discardResult: method(IdInput, TrainingJobSchema, {
|
|
49281
|
+
kind: "mutation",
|
|
49282
|
+
auth: "admin"
|
|
49283
|
+
})
|
|
49284
|
+
}
|
|
49285
|
+
};
|
|
47926
49286
|
function isValueOf(slot, value) {
|
|
47927
49287
|
return value.slot === slot;
|
|
47928
49288
|
}
|
|
@@ -50371,6 +51731,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
|
|
|
50371
51731
|
addonId: null,
|
|
50372
51732
|
access: "create"
|
|
50373
51733
|
},
|
|
51734
|
+
"backup.checkBackupKey": {
|
|
51735
|
+
capName: "backup",
|
|
51736
|
+
capScope: "system",
|
|
51737
|
+
addonId: null,
|
|
51738
|
+
access: "create"
|
|
51739
|
+
},
|
|
50374
51740
|
"backup.delete": {
|
|
50375
51741
|
capName: "backup",
|
|
50376
51742
|
capScope: "system",
|
|
@@ -50383,6 +51749,18 @@ var METHOD_ACCESS_MAP = Object.freeze({
|
|
|
50383
51749
|
addonId: null,
|
|
50384
51750
|
access: "delete"
|
|
50385
51751
|
},
|
|
51752
|
+
"backup.disableEncryption": {
|
|
51753
|
+
capName: "backup",
|
|
51754
|
+
capScope: "system",
|
|
51755
|
+
addonId: null,
|
|
51756
|
+
access: "create"
|
|
51757
|
+
},
|
|
51758
|
+
"backup.getEncryption": {
|
|
51759
|
+
capName: "backup",
|
|
51760
|
+
capScope: "system",
|
|
51761
|
+
addonId: null,
|
|
51762
|
+
access: "view"
|
|
51763
|
+
},
|
|
50386
51764
|
"backup.getEntries": {
|
|
50387
51765
|
capName: "backup",
|
|
50388
51766
|
capScope: "system",
|
|
@@ -50437,6 +51815,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
|
|
|
50437
51815
|
addonId: null,
|
|
50438
51816
|
access: "create"
|
|
50439
51817
|
},
|
|
51818
|
+
"backup.setupEncryption": {
|
|
51819
|
+
capName: "backup",
|
|
51820
|
+
capScope: "system",
|
|
51821
|
+
addonId: null,
|
|
51822
|
+
access: "create"
|
|
51823
|
+
},
|
|
50440
51824
|
"backup.trigger": {
|
|
50441
51825
|
capName: "backup",
|
|
50442
51826
|
capScope: "system",
|
|
@@ -50989,6 +52373,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
|
|
|
50989
52373
|
addonId: null,
|
|
50990
52374
|
access: "create"
|
|
50991
52375
|
},
|
|
52376
|
+
"customModelRegistry.importModelBundle": {
|
|
52377
|
+
capName: "custom-model-registry",
|
|
52378
|
+
capScope: "system",
|
|
52379
|
+
addonId: null,
|
|
52380
|
+
access: "create"
|
|
52381
|
+
},
|
|
50992
52382
|
"customModelRegistry.listModels": {
|
|
50993
52383
|
capName: "custom-model-registry",
|
|
50994
52384
|
capScope: "system",
|
|
@@ -52429,6 +53819,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
|
|
|
52429
53819
|
addonId: null,
|
|
52430
53820
|
access: "view"
|
|
52431
53821
|
},
|
|
53822
|
+
"keychainAdmin.acknowledgeReset": {
|
|
53823
|
+
capName: "keychain-admin",
|
|
53824
|
+
capScope: "system",
|
|
53825
|
+
addonId: null,
|
|
53826
|
+
access: "create"
|
|
53827
|
+
},
|
|
52432
53828
|
"keychainAdmin.deleteOrphans": {
|
|
52433
53829
|
capName: "keychain-admin",
|
|
52434
53830
|
capScope: "system",
|
|
@@ -52465,6 +53861,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
|
|
|
52465
53861
|
addonId: null,
|
|
52466
53862
|
access: "delete"
|
|
52467
53863
|
},
|
|
53864
|
+
"keychainAdmin.resetReport": {
|
|
53865
|
+
capName: "keychain-admin",
|
|
53866
|
+
capScope: "system",
|
|
53867
|
+
addonId: null,
|
|
53868
|
+
access: "delete"
|
|
53869
|
+
},
|
|
52468
53870
|
"keychainAdmin.reveal": {
|
|
52469
53871
|
capName: "keychain-admin",
|
|
52470
53872
|
capScope: "system",
|
|
@@ -54211,6 +55613,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
|
|
|
54211
55613
|
addonId: null,
|
|
54212
55614
|
access: "view"
|
|
54213
55615
|
},
|
|
55616
|
+
"pipelineAnalytics.offerRetrainArchive": {
|
|
55617
|
+
capName: "pipeline-analytics",
|
|
55618
|
+
capScope: "device",
|
|
55619
|
+
addonId: null,
|
|
55620
|
+
access: "create"
|
|
55621
|
+
},
|
|
54214
55622
|
"pipelineAnalytics.ownersWithMedia": {
|
|
54215
55623
|
capName: "pipeline-analytics",
|
|
54216
55624
|
capScope: "device",
|
|
@@ -56749,6 +58157,174 @@ var METHOD_ACCESS_MAP = Object.freeze({
|
|
|
56749
58157
|
addonId: null,
|
|
56750
58158
|
access: "view"
|
|
56751
58159
|
},
|
|
58160
|
+
"training.canaryResult": {
|
|
58161
|
+
capName: "training",
|
|
58162
|
+
capScope: "system",
|
|
58163
|
+
addonId: null,
|
|
58164
|
+
access: "create"
|
|
58165
|
+
},
|
|
58166
|
+
"training.cancelJob": {
|
|
58167
|
+
capName: "training",
|
|
58168
|
+
capScope: "system",
|
|
58169
|
+
addonId: null,
|
|
58170
|
+
access: "create"
|
|
58171
|
+
},
|
|
58172
|
+
"training.discardResult": {
|
|
58173
|
+
capName: "training",
|
|
58174
|
+
capScope: "system",
|
|
58175
|
+
addonId: null,
|
|
58176
|
+
access: "create"
|
|
58177
|
+
},
|
|
58178
|
+
"training.findDatasetVersionIdByName": {
|
|
58179
|
+
capName: "training",
|
|
58180
|
+
capScope: "system",
|
|
58181
|
+
addonId: null,
|
|
58182
|
+
access: "view"
|
|
58183
|
+
},
|
|
58184
|
+
"training.getDatasetVersion": {
|
|
58185
|
+
capName: "training",
|
|
58186
|
+
capScope: "system",
|
|
58187
|
+
addonId: null,
|
|
58188
|
+
access: "view"
|
|
58189
|
+
},
|
|
58190
|
+
"training.getJob": {
|
|
58191
|
+
capName: "training",
|
|
58192
|
+
capScope: "system",
|
|
58193
|
+
addonId: null,
|
|
58194
|
+
access: "view"
|
|
58195
|
+
},
|
|
58196
|
+
"training.getJobResult": {
|
|
58197
|
+
capName: "training",
|
|
58198
|
+
capScope: "system",
|
|
58199
|
+
addonId: null,
|
|
58200
|
+
access: "view"
|
|
58201
|
+
},
|
|
58202
|
+
"training.listBaseModels": {
|
|
58203
|
+
capName: "training",
|
|
58204
|
+
capScope: "system",
|
|
58205
|
+
addonId: null,
|
|
58206
|
+
access: "view"
|
|
58207
|
+
},
|
|
58208
|
+
"training.listDatasetVersions": {
|
|
58209
|
+
capName: "training",
|
|
58210
|
+
capScope: "system",
|
|
58211
|
+
addonId: null,
|
|
58212
|
+
access: "view"
|
|
58213
|
+
},
|
|
58214
|
+
"training.listJobs": {
|
|
58215
|
+
capName: "training",
|
|
58216
|
+
capScope: "system",
|
|
58217
|
+
addonId: null,
|
|
58218
|
+
access: "view"
|
|
58219
|
+
},
|
|
58220
|
+
"training.planJob": {
|
|
58221
|
+
capName: "training",
|
|
58222
|
+
capScope: "system",
|
|
58223
|
+
addonId: null,
|
|
58224
|
+
access: "view"
|
|
58225
|
+
},
|
|
58226
|
+
"training.putDatasetVersion": {
|
|
58227
|
+
capName: "training",
|
|
58228
|
+
capScope: "system",
|
|
58229
|
+
addonId: null,
|
|
58230
|
+
access: "create"
|
|
58231
|
+
},
|
|
58232
|
+
"training.registerResult": {
|
|
58233
|
+
capName: "training",
|
|
58234
|
+
capScope: "system",
|
|
58235
|
+
addonId: null,
|
|
58236
|
+
access: "create"
|
|
58237
|
+
},
|
|
58238
|
+
"training.removeDatasetVersion": {
|
|
58239
|
+
capName: "training",
|
|
58240
|
+
capScope: "system",
|
|
58241
|
+
addonId: null,
|
|
58242
|
+
access: "delete"
|
|
58243
|
+
},
|
|
58244
|
+
"training.rollbackCanary": {
|
|
58245
|
+
capName: "training",
|
|
58246
|
+
capScope: "system",
|
|
58247
|
+
addonId: null,
|
|
58248
|
+
access: "create"
|
|
58249
|
+
},
|
|
58250
|
+
"training.startJob": {
|
|
58251
|
+
capName: "training",
|
|
58252
|
+
capScope: "system",
|
|
58253
|
+
addonId: null,
|
|
58254
|
+
access: "create"
|
|
58255
|
+
},
|
|
58256
|
+
"trainingProvider.cancelJob": {
|
|
58257
|
+
capName: "training-provider",
|
|
58258
|
+
capScope: "system",
|
|
58259
|
+
addonId: null,
|
|
58260
|
+
access: "create"
|
|
58261
|
+
},
|
|
58262
|
+
"trainingProvider.cleanupJob": {
|
|
58263
|
+
capName: "training-provider",
|
|
58264
|
+
capScope: "system",
|
|
58265
|
+
addonId: null,
|
|
58266
|
+
access: "create"
|
|
58267
|
+
},
|
|
58268
|
+
"trainingProvider.describe": {
|
|
58269
|
+
capName: "training-provider",
|
|
58270
|
+
capScope: "system",
|
|
58271
|
+
addonId: null,
|
|
58272
|
+
access: "view"
|
|
58273
|
+
},
|
|
58274
|
+
"trainingProvider.estimateJob": {
|
|
58275
|
+
capName: "training-provider",
|
|
58276
|
+
capScope: "system",
|
|
58277
|
+
addonId: null,
|
|
58278
|
+
access: "view"
|
|
58279
|
+
},
|
|
58280
|
+
"trainingProvider.getJob": {
|
|
58281
|
+
capName: "training-provider",
|
|
58282
|
+
capScope: "system",
|
|
58283
|
+
addonId: null,
|
|
58284
|
+
access: "view"
|
|
58285
|
+
},
|
|
58286
|
+
"trainingProvider.listJobEvents": {
|
|
58287
|
+
capName: "training-provider",
|
|
58288
|
+
capScope: "system",
|
|
58289
|
+
addonId: null,
|
|
58290
|
+
access: "view"
|
|
58291
|
+
},
|
|
58292
|
+
"trainingProvider.listJobs": {
|
|
58293
|
+
capName: "training-provider",
|
|
58294
|
+
capScope: "system",
|
|
58295
|
+
addonId: null,
|
|
58296
|
+
access: "view"
|
|
58297
|
+
},
|
|
58298
|
+
"trainingProvider.offerArtifact": {
|
|
58299
|
+
capName: "training-provider",
|
|
58300
|
+
capScope: "system",
|
|
58301
|
+
addonId: null,
|
|
58302
|
+
access: "create"
|
|
58303
|
+
},
|
|
58304
|
+
"trainingProvider.readResult": {
|
|
58305
|
+
capName: "training-provider",
|
|
58306
|
+
capScope: "system",
|
|
58307
|
+
addonId: null,
|
|
58308
|
+
access: "create"
|
|
58309
|
+
},
|
|
58310
|
+
"trainingProvider.submitJob": {
|
|
58311
|
+
capName: "training-provider",
|
|
58312
|
+
capScope: "system",
|
|
58313
|
+
addonId: null,
|
|
58314
|
+
access: "create"
|
|
58315
|
+
},
|
|
58316
|
+
"trainingProvider.uploadDataset": {
|
|
58317
|
+
capName: "training-provider",
|
|
58318
|
+
capScope: "system",
|
|
58319
|
+
addonId: null,
|
|
58320
|
+
access: "create"
|
|
58321
|
+
},
|
|
58322
|
+
"trainingProvider.validateCredentials": {
|
|
58323
|
+
capName: "training-provider",
|
|
58324
|
+
capScope: "system",
|
|
58325
|
+
addonId: null,
|
|
58326
|
+
access: "create"
|
|
58327
|
+
},
|
|
56752
58328
|
"turnProvider.getTurnServers": {
|
|
56753
58329
|
capName: "turn-provider",
|
|
56754
58330
|
capScope: "system",
|
|
@@ -56773,12 +58349,6 @@ var METHOD_ACCESS_MAP = Object.freeze({
|
|
|
56773
58349
|
addonId: null,
|
|
56774
58350
|
access: "create"
|
|
56775
58351
|
},
|
|
56776
|
-
"userManagement.createApiKey": {
|
|
56777
|
-
capName: "user-management",
|
|
56778
|
-
capScope: "system",
|
|
56779
|
-
addonId: null,
|
|
56780
|
-
access: "create"
|
|
56781
|
-
},
|
|
56782
58352
|
"userManagement.createScopedToken": {
|
|
56783
58353
|
capName: "user-management",
|
|
56784
58354
|
capScope: "system",
|
|
@@ -56815,12 +58385,6 @@ var METHOD_ACCESS_MAP = Object.freeze({
|
|
|
56815
58385
|
addonId: null,
|
|
56816
58386
|
access: "view"
|
|
56817
58387
|
},
|
|
56818
|
-
"userManagement.linkExternalIdentity": {
|
|
56819
|
-
capName: "user-management",
|
|
56820
|
-
capScope: "system",
|
|
56821
|
-
addonId: null,
|
|
56822
|
-
access: "create"
|
|
56823
|
-
},
|
|
56824
58388
|
"userManagement.listApiKeys": {
|
|
56825
58389
|
capName: "user-management",
|
|
56826
58390
|
capScope: "system",
|
|
@@ -56923,12 +58487,6 @@ var METHOD_ACCESS_MAP = Object.freeze({
|
|
|
56923
58487
|
addonId: null,
|
|
56924
58488
|
access: "create"
|
|
56925
58489
|
},
|
|
56926
|
-
"userManagement.validateApiKey": {
|
|
56927
|
-
capName: "user-management",
|
|
56928
|
-
capScope: "system",
|
|
56929
|
-
addonId: null,
|
|
56930
|
-
access: "view"
|
|
56931
|
-
},
|
|
56932
58490
|
"userManagement.validateCredentials": {
|
|
56933
58491
|
capName: "user-management",
|
|
56934
58492
|
capScope: "system",
|
|
@@ -58921,6 +60479,8 @@ var ALL_CAPABILITY_DEFINITIONS = [
|
|
|
58921
60479
|
temperatureSensorCapability,
|
|
58922
60480
|
terminalSessionCapability,
|
|
58923
60481
|
toastCapability,
|
|
60482
|
+
trainingCapability,
|
|
60483
|
+
trainingProviderCapability,
|
|
58924
60484
|
turnProviderCapability,
|
|
58925
60485
|
updateCapability,
|
|
58926
60486
|
userManagementCapability,
|
|
@@ -61299,6 +62859,11 @@ Object.freeze({
|
|
|
61299
62859
|
form: "single",
|
|
61300
62860
|
optional: false
|
|
61301
62861
|
}],
|
|
62862
|
+
"training.canaryResult": [{
|
|
62863
|
+
name: "deviceIds",
|
|
62864
|
+
form: "array",
|
|
62865
|
+
optional: false
|
|
62866
|
+
}],
|
|
61302
62867
|
"update.getStatus": [{
|
|
61303
62868
|
name: "deviceId",
|
|
61304
62869
|
form: "single",
|
|
@@ -62936,7 +64501,12 @@ var CREDENTIAL_PARAM_PATTERNS = [
|
|
|
62936
64501
|
/^bearer$/i,
|
|
62937
64502
|
/^api[_-]?key$/i,
|
|
62938
64503
|
/^access[_-]?token$/i,
|
|
62939
|
-
/^refresh[_-]?token$/i
|
|
64504
|
+
/^refresh[_-]?token$/i,
|
|
64505
|
+
/^bridge$/i,
|
|
64506
|
+
/^code$/i,
|
|
64507
|
+
/^state$/i,
|
|
64508
|
+
/^challenge$/i,
|
|
64509
|
+
/^link$/i
|
|
62940
64510
|
];
|
|
62941
64511
|
function isCredentialParam(name) {
|
|
62942
64512
|
return CREDENTIAL_PARAM_PATTERNS.some((re) => re.test(name));
|
|
@@ -62958,6 +64528,10 @@ function isCredentialParam(name) {
|
|
|
62958
64528
|
* are NOT masked (those are entity references, not credentials).
|
|
62959
64529
|
*/
|
|
62960
64530
|
function maskUrlCredentials(rawUrl) {
|
|
64531
|
+
if (rawUrl.startsWith("/") && !rawUrl.startsWith("//")) {
|
|
64532
|
+
const masked = maskUrlCredentials(`http://relative.invalid${rawUrl}`);
|
|
64533
|
+
return masked.startsWith("http://relative.invalid") ? masked.slice(23) : rawUrl;
|
|
64534
|
+
}
|
|
62961
64535
|
try {
|
|
62962
64536
|
const u = new URL(rawUrl);
|
|
62963
64537
|
let touched = false;
|
|
@@ -63109,4 +64683,4 @@ function enumerateInferenceDevices(hw) {
|
|
|
63109
64683
|
return out;
|
|
63110
64684
|
}
|
|
63111
64685
|
//#endregion
|
|
63112
|
-
export {
|
|
64686
|
+
export { buildCapabilityLookup as $, compositionFeatureEnabled as $n, toNodeId as $r, logLevelAtMost as $t, METHOD_ACCESS_MAP as A, scopesAllowDeviceCap as An, expandCapMethods as Ar, expressionContainsBuiltinCall as At, StorageCleanupJobSchema as B, streamQualityLabel as Bn, nodePin as Br, isDefaultAppConnectionEndpoint as Bt, FieldClaimModeSchema as C, resolveCoreBlockFiles as Cn, collectSecretConfigKeys as Cr, deviceStatusCapability as Ct, LOG_LEVEL_RANK as D, resolveNotificationTapAddress as Dn, defineCustomActions as Dr, evaluateCompositionField as Dt, KEYCHAIN_RESET_TRIGGER_ACTOR as E, resolveMethodAuth as En, customAction as Er, enumerateSchemaFields as Et, RUNTIME_DEFAULTS as F, storageCapability as Fn, isKeychainStoredField as Fr, gbToBytes as Ft, addonPagesCapability as G, vectorIndexDimConflictError as Gn, readinessKey as Gr, keychainAdminCapability as Gt, StorageLocationTypeSchema as H, validateComposition as Hn, parseJsonObject as Hr, isObjectInput as Ht, RecordingStatusSchema as I, storageMigrationCapability as In, isSecretClear as Ir, hfModelUrl as It, alertsCapability as J, COMPOSER_ADDON_ID as Jn, scopeKey as Jr, legacyModeOf as Jt, addonSettingsCapability as K, vectorStoreCapability as Kn, resolveCapMount as Kr, keychainCapability as Kt, STORAGE_ACCESS_FALLBACK as L, storageOccupancyCapability as Ln, isSecretMarker as Lr, isAppLinkBase as Lt, MODEL_FORMATS as M, settingsStoreCapability as Mn, hydrateSchema as Mr, filesystemBrowseCapability as Mt, MOTION_CLOSE_AFTER_MS as N, siteZoneKey as Nn, isDeviceConfigCap as Nr, foldSnapshotByFunction as Nt, LegacyStorageLocationDefaultSchema as O, runtimeStatePolicyFor as On, emitDownForOwnedCaps as Or, evictionPolicyOfLocation as Ot, RECORDING_SOURCE_CAMSTACK_ADDON as P, snapshotCapability as Pn, isEvent as Pr, formatMinutes as Pt, batteryCapability as Q, composedDeviceStableId as Qn, sleep as Qr, logDestinationCapability as Qt, STREAM_PROFILE_META as R, storageProviderCapability as Rn, isSecretMarkerLike as Rr, isArrayOutputSchema as Rt, FIRST_LEVEL_MACRO_CLASSES as S, resolveCoreBlockEntry as Sn, buildSecretRef as Sr, deviceStateCapability as St, HF_REPO as T, resolveLocationMode as Tn, createEvent as Tr, enumerateItemArrayFields as Tt, StorageMigrationJobSchema as U, validateCustomAddress as Un, parseJsonUnknown as Ur, isVoidInput as Ut, StorageLocationModeSchema as V, userManagementCapability as Vn, normalizeSecretOwner as Vr, isLocationEnabled as Vt, UserRecordSchema as W, vectorDimFromBase64 as Wn, parseSecretRef as Wr, kebabToCamel as Wt, backupCapability as X, cloneStatefulExpressionMemory as Xn, secretTreeFromPaths as Xr, localNetworkCapability as Xt, authProviderCapability as Y, buildFfmpegArgs as Yn, scrubKnownSecrets as Yr, lifecycleJobSchema as Yt, bareAddonId as Z, composedDeviceRef as Zn, secretTreeFromSchema as Zr, locationIconKey as Zt, DATABASE_SNAPSHOT_TIMEOUT_MS as _, ptzAutotrackCapability as _n, SECRET_CLEAR as _r, defineExtensionSlotProvider as _t, BatteryStatusSchema as a, metricsProviderCapability as an, toExpressionValue as ar, compositionFieldKey as at, DeclaredDevices as b, resolveAppConnections as bn, asNumber as br, deviceExtensionCapability as bt, CAP_NAMES_WITH_STATUS as c, normalizeCoreBlockWrite as cn, DeviceType as cr, coreBlockComposition as ct, CORE_BLOCKS_ADDON_ID as d, objectInputDeclaresRequiredDeviceId as dn, DATAPLANE_SECRET_HEADER as dr, coreBlocksCapability as dt, toSecretFieldView as ei, looseSchema as en, compositionItemFieldPath as er, buildStreamParamsConfigSchema as et, CORE_BLOCK_ADDON_PREFIX as f, parseStreamParamsFormPatch as fn, DEVICE_SETTINGS_CONTRIBUTION_METHODS as fr, createDeviceExtensionProvider as ft, DATABASE_SNAPSHOT_SUFFIX as g, procedureAuthKey as gn, ReadinessTimeoutError as gr, decodeVectorBase64 as gt, DATABASE_SNAPSHOT_PARTIAL_SUFFIX as h, platformProbeCapability as hn, ReadinessRegistry as hr, declarationOwnerNodeId as ht, BaseDevice as i, memoizePinnedFfmpeg as in, disarmRoseMemory as ir, composerCapability as it, MINUTES_PER_DAY as j, scoreRuntimes as jn, formatSecretScope as jr, extractNestedAddonId as jt, LocationIconIdSchema as k, scopesAllowAddon as kn, emitReadiness as kr, expandContainer as kt, COMPOSED_ITEM_FIXED_FIELDS as l, normalizeUnit as ln, errMsg as lr, coreBlockIdFromAddonId as lt, CoreBlockSchema as m, planComposition as mn, REDACTED_SECRET as mr, databaseSnapshotCapability as mt, AlertSchema as n, EventCategory as ni, mayReadLocation as nn, compositionSourceKey as nr, cameraGridLayoutCapability as nt, CAMERA_SWITCH_ORDER as o, modeMayWrite as on, DeviceFeature as or, compositionSourceRefs as ot, CalendarPreferencesSchema as p, pickAppLinkHub as pn, DEVICE_STATUS_METHOD as pr, dataStoreProviderCapability as pt, addonWidgetsCapability as q, withLocationMode as qn, resolveSecretMarkers as qr, legacyAutoLinkIds as qt, ApiKeyRecordSchema as r, mayWriteToLocation as rn, createStatefulExpressionMemory as rr, checkRoleAddress as rt, CAMSTACK_APP_LINK_ORIGIN as s, motionCapability as sn, DeviceRole as sr, coreBlockAddonId as st, ALL_CAPABILITY_DEFINITIONS as t, transformSecretWrite as ti, maskUrlCredentials as tn, compositionSliceKey as tr, calendarsCapability as tt, COMPOSER_RUNNER_ID as u, objectInputDeclaresAddonId as un, BaseAddon as ur, coreBlockSourceKind as ut, DAY_NAMES as v, reducePoints as vn, WELL_KNOWN_TAB_MAP as vr, deriveBatteryPresence as vt, FieldClaimSchema as w, resolveHubAddressRoles as wn, collectZodSecretPaths as wr, enumerateInferenceDevices as wt, DeviceStatusSchema as x, resolveBucketMs as xn, asString$1 as xr, deviceManagerCapability as xt, DERIVED_CAMERA_SILENCED_CAP_NAMES as y, replacedFieldKeys as yn, asJsonObject as yr, describeComposableCaps as yt, ScopedTokenSchema as z, streamCatalogCapability as zn, makeSecretMarker as zr, isCollectionArrayMethod as zt };
|