@camstack/addon-waste-collections 0.1.35 → 0.1.37
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/CHANGELOG.md +14 -0
- package/dist/waste-collections.addon.js +181 -733
- package/dist/waste-collections.addon.mjs +181 -733
- package/package.json +2 -2
|
@@ -6,7 +6,7 @@ let node_fs = require("node:fs");
|
|
|
6
6
|
let node_path = require("node:path");
|
|
7
7
|
let node_child_process = require("node:child_process");
|
|
8
8
|
let node_url = require("node:url");
|
|
9
|
-
//#region ../types/dist/event-category-
|
|
9
|
+
//#region ../types/dist/event-category-78tcBemo.mjs
|
|
10
10
|
var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
11
11
|
EventCategory["SystemBoot"] = "system.boot";
|
|
12
12
|
EventCategory["SystemAddonsReady"] = "system.addons-ready";
|
|
@@ -127,6 +127,23 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
|
127
127
|
*/
|
|
128
128
|
EventCategory["AddonLoadRecovered"] = "addon.load-recovered";
|
|
129
129
|
/**
|
|
130
|
+
* An addon is RUNNING although its installed package is below what the
|
|
131
|
+
* running server requires (`camstack.requires.addons` of the server, or the
|
|
132
|
+
* addon's own `requires.server`). Raised once per streak by the unsupported-
|
|
133
|
+
* addon tracker, on the hub and on agents; AlertCenter turns it into one
|
|
134
|
+
* operator alert per node and package. The addon is never held back for it.
|
|
135
|
+
*
|
|
136
|
+
* Payload: `{ packageName, addonId, nodeId, installedVersion, requiredMinimum, serverVersion, unmet }`.
|
|
137
|
+
*/
|
|
138
|
+
EventCategory["AddonUnsupportedVersion"] = "addon.unsupported-version";
|
|
139
|
+
/**
|
|
140
|
+
* The package of an {@link AddonUnsupportedVersion} alert reached a supported
|
|
141
|
+
* version, or is no longer installed on the node. AlertCenter dismisses the matching alert.
|
|
142
|
+
*
|
|
143
|
+
* Payload: `{ packageName, addonId, nodeId }`.
|
|
144
|
+
*/
|
|
145
|
+
EventCategory["AddonUnsupportedResolved"] = "addon.unsupported-resolved";
|
|
146
|
+
/**
|
|
130
147
|
* Monitor scheduled the next retry for a failed addon. Transient —
|
|
131
148
|
* surfaced to the UI for live-updating the "next retry in Ns"
|
|
132
149
|
* countdown on the Addons page row, NOT persisted as an alert.
|
|
@@ -237,7 +254,7 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
|
237
254
|
EventCategory["ProcessRestarted"] = "process.restarted";
|
|
238
255
|
/**
|
|
239
256
|
* The SET of storage locations changed — one was created, edited, enabled,
|
|
240
|
-
* disabled or deleted through
|
|
257
|
+
* disabled or deleted through the location instances or the forced-delete action.
|
|
241
258
|
*
|
|
242
259
|
* Telemetry, not a transaction (D8/D11): every consumer that re-resolves on
|
|
243
260
|
* it must also converge on its own periodic path, because a dropped event
|
|
@@ -5988,7 +6005,7 @@ var ZodIssueCode = {
|
|
|
5988
6005
|
var ZodFirstPartyTypeKind;
|
|
5989
6006
|
ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
|
|
5990
6007
|
//#endregion
|
|
5991
|
-
//#region ../types/dist/sleep-
|
|
6008
|
+
//#region ../types/dist/sleep-AEnTN3b4.mjs
|
|
5992
6009
|
/**
|
|
5993
6010
|
* The audio chunk plane's byte format, and the ONE expansion from a coded
|
|
5994
6011
|
* window to float samples (D455).
|
|
@@ -6083,16 +6100,6 @@ var AddonMigrationsBlockedError = class extends Error {
|
|
|
6083
6100
|
this.migrationIds = migrationIds;
|
|
6084
6101
|
}
|
|
6085
6102
|
};
|
|
6086
|
-
/**
|
|
6087
|
-
* The value a redacted secret is replaced with on every read surface.
|
|
6088
|
-
*
|
|
6089
|
-
* It is also the WRITE-BACK token: an `upsertLocation` that sends this value
|
|
6090
|
-
* back for a key means "keep what is stored", which is what lets an operator
|
|
6091
|
-
* rename a location without retyping its password. A literal an operator could
|
|
6092
|
-
* plausibly choose as a real password would turn that convenience into a way
|
|
6093
|
-
* to lock yourself out, hence the sentinel shape.
|
|
6094
|
-
*/
|
|
6095
|
-
var REDACTED_SECRET = "__camstack_redacted__";
|
|
6096
6103
|
/** Is this field's stored value a credential? */
|
|
6097
6104
|
function isSecretConfigField(field) {
|
|
6098
6105
|
if (field.type === "password") return true;
|
|
@@ -6734,10 +6741,33 @@ var RECORDS_ERROR_CODES = [
|
|
|
6734
6741
|
"transact-refused"
|
|
6735
6742
|
];
|
|
6736
6743
|
var PREFIX = "records/";
|
|
6744
|
+
/**
|
|
6745
|
+
* Cross-realm brand. An addon bundles its own copy of this module (D15) while
|
|
6746
|
+
* `ctx.records` is the HOST's door, so an error thrown by the host is an
|
|
6747
|
+
* instance of the host's classes, not of the addon's. The brand plus the code
|
|
6748
|
+
* make `instanceof` answer by identity of the failure, not of the copy.
|
|
6749
|
+
*/
|
|
6750
|
+
var RECORDS_ERROR_BRAND = Symbol.for("camstack.records-error");
|
|
6751
|
+
function carriesBrand(value) {
|
|
6752
|
+
return typeof value === "object" && value !== null && Reflect.get(value, RECORDS_ERROR_BRAND) === true;
|
|
6753
|
+
}
|
|
6737
6754
|
/** The base of every records failure. `detail` is the human part of the message. */
|
|
6738
6755
|
var RecordsError = class extends Error {
|
|
6756
|
+
/** The code a subclass stands for; the base stands for every code. */
|
|
6757
|
+
static code = void 0;
|
|
6758
|
+
/**
|
|
6759
|
+
* `instanceof` across copies of this module: any branded error is a
|
|
6760
|
+
* `RecordsError`, and is an instance of a subclass only when its code is the
|
|
6761
|
+
* subclass's code. Same-copy answers are unchanged.
|
|
6762
|
+
*/
|
|
6763
|
+
static [Symbol.hasInstance](value) {
|
|
6764
|
+
if (Function.prototype[Symbol.hasInstance].call(this, value)) return true;
|
|
6765
|
+
if (!carriesBrand(value)) return false;
|
|
6766
|
+
return this.code === void 0 || value.code === this.code;
|
|
6767
|
+
}
|
|
6739
6768
|
code;
|
|
6740
6769
|
detail;
|
|
6770
|
+
[RECORDS_ERROR_BRAND] = true;
|
|
6741
6771
|
constructor(code, detail) {
|
|
6742
6772
|
super(`${PREFIX}${code}: ${detail}`);
|
|
6743
6773
|
this.name = "RecordsError";
|
|
@@ -6746,96 +6776,110 @@ var RecordsError = class extends Error {
|
|
|
6746
6776
|
}
|
|
6747
6777
|
};
|
|
6748
6778
|
/** A write guarded by `ifRev` found another revision (or an existing row for `ifRev: 0`). */
|
|
6749
|
-
var RevConflictError = class extends RecordsError {
|
|
6779
|
+
var RevConflictError = class RevConflictError extends RecordsError {
|
|
6780
|
+
static code = "rev-conflict";
|
|
6750
6781
|
constructor(detail) {
|
|
6751
|
-
super(
|
|
6782
|
+
super(RevConflictError.code, detail);
|
|
6752
6783
|
this.name = "RevConflictError";
|
|
6753
6784
|
}
|
|
6754
6785
|
};
|
|
6755
6786
|
/** `data` exceeds the kind's `maxDataBytes`. Bytes are never record fields (D613). */
|
|
6756
|
-
var RecordTooLargeError = class extends RecordsError {
|
|
6787
|
+
var RecordTooLargeError = class RecordTooLargeError extends RecordsError {
|
|
6788
|
+
static code = "too-large";
|
|
6757
6789
|
constructor(detail) {
|
|
6758
|
-
super(
|
|
6790
|
+
super(RecordTooLargeError.code, detail);
|
|
6759
6791
|
this.name = "RecordTooLargeError";
|
|
6760
6792
|
}
|
|
6761
6793
|
};
|
|
6762
6794
|
/** A filter, order or aggregate names a field no declared index serves. */
|
|
6763
|
-
var UnindexedFilterError = class extends RecordsError {
|
|
6795
|
+
var UnindexedFilterError = class UnindexedFilterError extends RecordsError {
|
|
6796
|
+
static code = "unindexed-filter";
|
|
6764
6797
|
constructor(detail) {
|
|
6765
|
-
super(
|
|
6798
|
+
super(UnindexedFilterError.code, detail);
|
|
6766
6799
|
this.name = "UnindexedFilterError";
|
|
6767
6800
|
}
|
|
6768
6801
|
};
|
|
6769
6802
|
/** A bulk delete with no predicate at all. */
|
|
6770
|
-
var FilterRequiredError = class extends RecordsError {
|
|
6803
|
+
var FilterRequiredError = class FilterRequiredError extends RecordsError {
|
|
6804
|
+
static code = "filter-required";
|
|
6771
6805
|
constructor(detail) {
|
|
6772
|
-
super(
|
|
6806
|
+
super(FilterRequiredError.code, detail);
|
|
6773
6807
|
this.name = "FilterRequiredError";
|
|
6774
6808
|
}
|
|
6775
6809
|
};
|
|
6776
6810
|
/** The caller declared no such kind (another addon's kind is indistinguishable from none). */
|
|
6777
|
-
var UnknownRecordKindError = class extends RecordsError {
|
|
6811
|
+
var UnknownRecordKindError = class UnknownRecordKindError extends RecordsError {
|
|
6812
|
+
static code = "unknown-kind";
|
|
6778
6813
|
constructor(detail) {
|
|
6779
|
-
super(
|
|
6814
|
+
super(UnknownRecordKindError.code, detail);
|
|
6780
6815
|
this.name = "UnknownRecordKindError";
|
|
6781
6816
|
}
|
|
6782
6817
|
};
|
|
6783
|
-
var RecordNotFoundError = class extends RecordsError {
|
|
6818
|
+
var RecordNotFoundError = class RecordNotFoundError extends RecordsError {
|
|
6819
|
+
static code = "not-found";
|
|
6784
6820
|
constructor(detail) {
|
|
6785
|
-
super(
|
|
6821
|
+
super(RecordNotFoundError.code, detail);
|
|
6786
6822
|
this.name = "RecordNotFoundError";
|
|
6787
6823
|
}
|
|
6788
6824
|
};
|
|
6789
6825
|
/** A unique index refused the write. */
|
|
6790
|
-
var RecordUniqueViolationError = class extends RecordsError {
|
|
6826
|
+
var RecordUniqueViolationError = class RecordUniqueViolationError extends RecordsError {
|
|
6827
|
+
static code = "unique-violation";
|
|
6791
6828
|
constructor(detail) {
|
|
6792
|
-
super(
|
|
6829
|
+
super(RecordUniqueViolationError.code, detail);
|
|
6793
6830
|
this.name = "RecordUniqueViolationError";
|
|
6794
6831
|
}
|
|
6795
6832
|
};
|
|
6796
6833
|
/** A secret field held a plain string where only a keychain marker may be stored. */
|
|
6797
|
-
var PlaintextSecretError = class extends RecordsError {
|
|
6834
|
+
var PlaintextSecretError = class PlaintextSecretError extends RecordsError {
|
|
6835
|
+
static code = "plaintext-secret";
|
|
6798
6836
|
constructor(detail) {
|
|
6799
|
-
super(
|
|
6837
|
+
super(PlaintextSecretError.code, detail);
|
|
6800
6838
|
this.name = "PlaintextSecretError";
|
|
6801
6839
|
}
|
|
6802
6840
|
};
|
|
6803
6841
|
/** A marker whose ref is not the row's own (the D878 rule). */
|
|
6804
|
-
var ForeignSecretMarkerError = class extends RecordsError {
|
|
6842
|
+
var ForeignSecretMarkerError = class ForeignSecretMarkerError extends RecordsError {
|
|
6843
|
+
static code = "foreign-marker";
|
|
6805
6844
|
constructor(detail) {
|
|
6806
|
-
super(
|
|
6845
|
+
super(ForeignSecretMarkerError.code, detail);
|
|
6807
6846
|
this.name = "ForeignSecretMarkerError";
|
|
6808
6847
|
}
|
|
6809
6848
|
};
|
|
6810
6849
|
/** The keychain could not store or serve a value: explicit, never an empty string. */
|
|
6811
|
-
var RecordSecretUnavailableError = class extends RecordsError {
|
|
6850
|
+
var RecordSecretUnavailableError = class RecordSecretUnavailableError extends RecordsError {
|
|
6851
|
+
static code = "secret-unavailable";
|
|
6812
6852
|
constructor(detail) {
|
|
6813
|
-
super(
|
|
6853
|
+
super(RecordSecretUnavailableError.code, detail);
|
|
6814
6854
|
this.name = "RecordSecretUnavailableError";
|
|
6815
6855
|
}
|
|
6816
6856
|
};
|
|
6817
|
-
var InvalidRecordError = class extends RecordsError {
|
|
6857
|
+
var InvalidRecordError = class InvalidRecordError extends RecordsError {
|
|
6858
|
+
static code = "invalid-record";
|
|
6818
6859
|
constructor(detail) {
|
|
6819
|
-
super(
|
|
6860
|
+
super(InvalidRecordError.code, detail);
|
|
6820
6861
|
this.name = "InvalidRecordError";
|
|
6821
6862
|
}
|
|
6822
6863
|
};
|
|
6823
6864
|
/** A kind declaration the engine refuses (non-additive change, bad shape, wrong file). */
|
|
6824
|
-
var DeclarationRefusedError = class extends RecordsError {
|
|
6865
|
+
var DeclarationRefusedError = class DeclarationRefusedError extends RecordsError {
|
|
6866
|
+
static code = "declaration-refused";
|
|
6825
6867
|
constructor(detail) {
|
|
6826
|
-
super(
|
|
6868
|
+
super(DeclarationRefusedError.code, detail);
|
|
6827
6869
|
this.name = "DeclarationRefusedError";
|
|
6828
6870
|
}
|
|
6829
6871
|
};
|
|
6830
|
-
var NoCallerError = class extends RecordsError {
|
|
6872
|
+
var NoCallerError = class NoCallerError extends RecordsError {
|
|
6873
|
+
static code = "no-caller";
|
|
6831
6874
|
constructor(detail) {
|
|
6832
|
-
super(
|
|
6875
|
+
super(NoCallerError.code, detail);
|
|
6833
6876
|
this.name = "NoCallerError";
|
|
6834
6877
|
}
|
|
6835
6878
|
};
|
|
6836
|
-
var BatchTooLargeError = class extends RecordsError {
|
|
6879
|
+
var BatchTooLargeError = class BatchTooLargeError extends RecordsError {
|
|
6880
|
+
static code = "batch-too-large";
|
|
6837
6881
|
constructor(detail) {
|
|
6838
|
-
super(
|
|
6882
|
+
super(BatchTooLargeError.code, detail);
|
|
6839
6883
|
this.name = "BatchTooLargeError";
|
|
6840
6884
|
}
|
|
6841
6885
|
};
|
|
@@ -6844,9 +6888,10 @@ var BatchTooLargeError = class extends RecordsError {
|
|
|
6844
6888
|
* caller did not declare in ONE file and ONE `transactFamily`. The detail names
|
|
6845
6889
|
* which rule (`cross-file`, `cross-family`, `no-family`); it is never a silent split.
|
|
6846
6890
|
*/
|
|
6847
|
-
var TransactRefusedError = class extends RecordsError {
|
|
6891
|
+
var TransactRefusedError = class TransactRefusedError extends RecordsError {
|
|
6892
|
+
static code = "transact-refused";
|
|
6848
6893
|
constructor(detail) {
|
|
6849
|
-
super(
|
|
6894
|
+
super(TransactRefusedError.code, detail);
|
|
6850
6895
|
this.name = "TransactRefusedError";
|
|
6851
6896
|
}
|
|
6852
6897
|
};
|
|
@@ -7365,17 +7410,35 @@ var RecordIntegrationInstances = class {
|
|
|
7365
7410
|
...kind !== void 0 ? { kind } : {}
|
|
7366
7411
|
});
|
|
7367
7412
|
if (minted === void 0) throw new Error("createInstance needs an id: this provider does not mint one");
|
|
7413
|
+
const initial = this.hooks.initialBody(name, kind);
|
|
7414
|
+
const patch = this.patchOf(values ?? {});
|
|
7415
|
+
if (Object.keys(patch).length > 0) Object.assign(patch, await this.hooks.validateWrite?.({
|
|
7416
|
+
kind,
|
|
7417
|
+
body: {
|
|
7418
|
+
...initial,
|
|
7419
|
+
...patch
|
|
7420
|
+
}
|
|
7421
|
+
}));
|
|
7368
7422
|
await this.handle.put({
|
|
7369
7423
|
id: minted,
|
|
7370
|
-
data:
|
|
7424
|
+
data: initial
|
|
7371
7425
|
}, { ifRev: 0 });
|
|
7372
|
-
const patch = this.patchOf(values ?? {});
|
|
7373
7426
|
if (Object.keys(patch).length > 0) await this.handle.patch(minted, patch);
|
|
7374
7427
|
await this.hooks.onChanged?.(minted, "created");
|
|
7375
7428
|
return { id: minted };
|
|
7376
7429
|
};
|
|
7377
7430
|
updateInstance = async ({ id, name, values }) => {
|
|
7378
7431
|
const patch = this.patchOf(values);
|
|
7432
|
+
if (this.hooks.validateWrite !== void 0 && Object.keys(patch).length > 0) {
|
|
7433
|
+
const stored = await this.handle.get(id);
|
|
7434
|
+
if (stored !== null) Object.assign(patch, await this.hooks.validateWrite({
|
|
7435
|
+
kind: void 0,
|
|
7436
|
+
body: {
|
|
7437
|
+
...stored.data,
|
|
7438
|
+
...patch
|
|
7439
|
+
}
|
|
7440
|
+
}));
|
|
7441
|
+
}
|
|
7379
7442
|
if (name !== void 0) patch[this.nameField] = name;
|
|
7380
7443
|
await this.handle.patch(id, patch);
|
|
7381
7444
|
await this.hooks.onChanged?.(id, "updated");
|
|
@@ -10739,7 +10802,6 @@ var CAP_NAMES = Object.freeze([
|
|
|
10739
10802
|
"notifier",
|
|
10740
10803
|
"numeric-sensor",
|
|
10741
10804
|
"oauth-integration",
|
|
10742
|
-
"oidc-connections",
|
|
10743
10805
|
"osd",
|
|
10744
10806
|
"osd-manager",
|
|
10745
10807
|
"pet-feeder",
|
|
@@ -12631,8 +12693,7 @@ Object.freeze({
|
|
|
12631
12693
|
"device-provider": { "reloadDevice": 18e4 },
|
|
12632
12694
|
"llm": {
|
|
12633
12695
|
"generate": 6e5,
|
|
12634
|
-
"generateVision": 6e5
|
|
12635
|
-
"testProfile": 6e5
|
|
12696
|
+
"generateVision": 6e5
|
|
12636
12697
|
},
|
|
12637
12698
|
"llm-runtime": {
|
|
12638
12699
|
"complete": 6e5,
|
|
@@ -14035,17 +14096,18 @@ var IntegrationInstanceRefSchema = object({
|
|
|
14035
14096
|
* message to what the kind supports — callers never special-case a service.
|
|
14036
14097
|
*
|
|
14037
14098
|
* DESIGN DECISIONS (locked):
|
|
14038
|
-
* -
|
|
14039
|
-
*
|
|
14040
|
-
*
|
|
14041
|
-
*
|
|
14042
|
-
*
|
|
14043
|
-
*
|
|
14044
|
-
*
|
|
14045
|
-
*
|
|
14046
|
-
*
|
|
14047
|
-
* routable. `send` / `testTarget` /
|
|
14048
|
-
* `
|
|
14099
|
+
* - A target is an INSTANCE of this function cap (epic #440, step 6): the
|
|
14100
|
+
* providers serve their targets as `integration-instances` records, one
|
|
14101
|
+
* record kind per target kind, and the admin lists, adds, edits, switches
|
|
14102
|
+
* and deletes them through that cap. This cap keeps what an instance cannot
|
|
14103
|
+
* say: the kind catalogue (`listTargetKinds`: capabilities, delivery
|
|
14104
|
+
* parameters, brand icon), dispatch (`send`, `testTarget`) and the form
|
|
14105
|
+
* helpers (`discoverTargets`, `probeTargetConfig`).
|
|
14106
|
+
* - `listTargetKinds` / `discoverTargets` return arrays → the generated
|
|
14107
|
+
* cap-mount auto-`concatCollection`-fans them across every registered
|
|
14108
|
+
* provider so one catalog is routable. `send` / `testTarget` /
|
|
14109
|
+
* `probeTargetConfig` route to ONE provider by the `addonId` the generated
|
|
14110
|
+
* collection router extracts from the call input.
|
|
14049
14111
|
* - `Attachment.bytes` is `Uint8Array`. Transport-safe: superjson (the tRPC
|
|
14050
14112
|
* transformer) + UDS MsgPack both round-trip typed arrays — already used by
|
|
14051
14113
|
* `storage` / `storage-provider` / `recording` caps over the same path. No
|
|
@@ -14290,7 +14352,7 @@ var TargetKindCapsSchema = object({
|
|
|
14290
14352
|
* the union is large and not meant for runtime validation here; the exported
|
|
14291
14353
|
* `TargetKind` type re-tightens `configSchema` to `ConfigUISchema`.
|
|
14292
14354
|
*/
|
|
14293
|
-
var ConfigSchemaPassthrough$
|
|
14355
|
+
var ConfigSchemaPassthrough$3 = unknown();
|
|
14294
14356
|
var TargetKindSchema = object({
|
|
14295
14357
|
kind: string(),
|
|
14296
14358
|
label: string(),
|
|
@@ -14327,7 +14389,7 @@ var TargetKindSchema = object({
|
|
|
14327
14389
|
* its raster path, which is the safe default for an unknown image.
|
|
14328
14390
|
*/
|
|
14329
14391
|
iconMediaType: string().optional(),
|
|
14330
|
-
configSchema: ConfigSchemaPassthrough$
|
|
14392
|
+
configSchema: ConfigSchemaPassthrough$3,
|
|
14331
14393
|
/**
|
|
14332
14394
|
* The config keys that decide WHERE this kind's secrets are sent (server,
|
|
14333
14395
|
* base URL, instance, chat, recipient). A save that changes any of them
|
|
@@ -14349,9 +14411,11 @@ var TargetKindSchema = object({
|
|
|
14349
14411
|
caps: TargetKindCapsSchema
|
|
14350
14412
|
});
|
|
14351
14413
|
/**
|
|
14352
|
-
* A
|
|
14353
|
-
*
|
|
14354
|
-
*
|
|
14414
|
+
* A target as a provider's own code sees it (the notification-center bridge, the
|
|
14415
|
+
* send path): the flat record body split into the name, the switch and the
|
|
14416
|
+
* kind's fields. `config` holds secrets; a reader that serves it to a client
|
|
14417
|
+
* REDACTS secret fields (a presence marker only) — never round-trip a stored
|
|
14418
|
+
* secret to the UI.
|
|
14355
14419
|
*/
|
|
14356
14420
|
var TargetSchema = object({
|
|
14357
14421
|
id: string(),
|
|
@@ -14394,13 +14458,13 @@ var ProbeTargetConfigResultSchema = object({
|
|
|
14394
14458
|
stateInstance: string().optional(),
|
|
14395
14459
|
error: string().optional()
|
|
14396
14460
|
});
|
|
14397
|
-
method(object({}), array(TargetKindSchema)), method(object({
|
|
14461
|
+
method(object({}), array(TargetKindSchema)), method(object({
|
|
14398
14462
|
kind: string(),
|
|
14399
14463
|
config: record(string(), unknown()).optional(),
|
|
14400
14464
|
/**
|
|
14401
14465
|
* When editing / duplicating, the blank password field never round-trips
|
|
14402
14466
|
* the stored token. Pass the source target id so the provider can restore
|
|
14403
|
-
* secrets server-side before dialling
|
|
14467
|
+
* secrets server-side before dialling.
|
|
14404
14468
|
*/
|
|
14405
14469
|
targetId: string().optional()
|
|
14406
14470
|
}), array(DiscoveredTargetSchema), { auth: "admin" }), method(object({
|
|
@@ -14423,27 +14487,6 @@ method(object({}), array(TargetKindSchema)), method(object({}), array(TargetSche
|
|
|
14423
14487
|
}), TestResultSchema, {
|
|
14424
14488
|
kind: "mutation",
|
|
14425
14489
|
auth: "admin"
|
|
14426
|
-
}), method(object({
|
|
14427
|
-
target: TargetSchema,
|
|
14428
|
-
/**
|
|
14429
|
-
* Duplicate flow: create a NEW target id while copying secrets from an
|
|
14430
|
-
* existing row. The UI never holds the stored token; blank / redacted
|
|
14431
|
-
* password fields are restored from this source (D634 still applies if
|
|
14432
|
-
* destinations diverge without a re-typed secret).
|
|
14433
|
-
*/
|
|
14434
|
-
cloneSecretsFrom: string().optional()
|
|
14435
|
-
}), TargetSchema, {
|
|
14436
|
-
kind: "mutation",
|
|
14437
|
-
auth: "admin"
|
|
14438
|
-
}), method(object({ targetId: string() }), _void(), {
|
|
14439
|
-
kind: "mutation",
|
|
14440
|
-
auth: "admin"
|
|
14441
|
-
}), method(object({
|
|
14442
|
-
targetId: string(),
|
|
14443
|
-
enabled: boolean()
|
|
14444
|
-
}), _void(), {
|
|
14445
|
-
kind: "mutation",
|
|
14446
|
-
auth: "admin"
|
|
14447
14490
|
});
|
|
14448
14491
|
looseObject({
|
|
14449
14492
|
name: string().min(1),
|
|
@@ -22701,7 +22744,6 @@ var SYSTEM_CAP_VOCABULARY = {
|
|
|
22701
22744
|
"notification-output": system("notification delivery — reached through notifier devices and rules"),
|
|
22702
22745
|
"notification-rules": system("notification rules — authored on their own page"),
|
|
22703
22746
|
"oauth-integration": system(IDENTITY),
|
|
22704
|
-
"oidc-connections": system(IDENTITY),
|
|
22705
22747
|
"osd-manager": system(PIPELINE),
|
|
22706
22748
|
"pipeline-executor": system(PIPELINE),
|
|
22707
22749
|
"pipeline-orchestrator": system(PIPELINE),
|
|
@@ -54389,7 +54431,7 @@ method(_void(), array(IntegrationWithStateSchema)), method(object({ id: string()
|
|
|
54389
54431
|
* or the redaction token KEEPS the stored secret; `{ $secretClear: true }`
|
|
54390
54432
|
* clears it.
|
|
54391
54433
|
*/
|
|
54392
|
-
var ConfigSchemaPassthrough$
|
|
54434
|
+
var ConfigSchemaPassthrough$2 = custom();
|
|
54393
54435
|
var IntegrationInstanceIdSchema = string().min(1);
|
|
54394
54436
|
var IntegrationInstanceModeSchema = _enum(["single", "multiple"]);
|
|
54395
54437
|
/**
|
|
@@ -54435,7 +54477,16 @@ var IntegrationInstanceKindSchema = object({
|
|
|
54435
54477
|
/** Relative hub path to start an interactive account link of an instance of this kind. */
|
|
54436
54478
|
connectPath: string().min(1).optional(),
|
|
54437
54479
|
/** Relative hub path to disconnect that account. */
|
|
54438
|
-
disconnectPath: string().min(1).optional()
|
|
54480
|
+
disconnectPath: string().min(1).optional(),
|
|
54481
|
+
/**
|
|
54482
|
+
* Parts of the edit dialog the flat form cannot draw (a node picker, a model source), named by a
|
|
54483
|
+
* GENERIC widget id the client maps to a component; a client that knows none of them shows none.
|
|
54484
|
+
* `connection` widgets write connection values, `model` widgets own the model choice.
|
|
54485
|
+
*/
|
|
54486
|
+
widgets: array(object({
|
|
54487
|
+
id: string().min(1),
|
|
54488
|
+
placement: _enum(["connection", "model"])
|
|
54489
|
+
})).optional()
|
|
54439
54490
|
});
|
|
54440
54491
|
/**
|
|
54441
54492
|
* How devices of an instance are found. `none`: the operator adds them by hand; `lan`: the addon
|
|
@@ -54540,7 +54591,7 @@ var integrationInstancesCapability = {
|
|
|
54540
54591
|
id: IntegrationInstanceIdSchema.nullable(),
|
|
54541
54592
|
/** The kind of the create form (`id: null`) of a provider with several. */
|
|
54542
54593
|
kind: string().min(1).optional()
|
|
54543
|
-
}), ConfigSchemaPassthrough$
|
|
54594
|
+
}), ConfigSchemaPassthrough$2, { auth: "admin" }),
|
|
54544
54595
|
listInstances: method(_void(), array(IntegrationInstanceSchema).readonly(), { auth: "admin" }),
|
|
54545
54596
|
getInstance: method(object({ id: IntegrationInstanceIdSchema }), IntegrationInstanceSchema.nullable(), { auth: "admin" }),
|
|
54546
54597
|
/**
|
|
@@ -55491,12 +55542,12 @@ var LlmModelInfoSchema = object({
|
|
|
55491
55542
|
});
|
|
55492
55543
|
/**
|
|
55493
55544
|
* The provider-specific part of a profile, validated by the provider that owns
|
|
55494
|
-
* the kind (
|
|
55545
|
+
* the kind (saving an instance refuses what it does not accept). A field its
|
|
55495
55546
|
* `connectionSchema` declares `type: 'password'` is a secret: it is held in the
|
|
55496
55547
|
* keychain, never in the row, and reads back as the redaction marker.
|
|
55497
55548
|
*/
|
|
55498
55549
|
var LlmProviderConfigSchema = record(string(), unknown());
|
|
55499
|
-
|
|
55550
|
+
object({
|
|
55500
55551
|
id: string(),
|
|
55501
55552
|
name: string(),
|
|
55502
55553
|
/** The provider-declared kind (`LlmProfileKindDescriptor.kind`). */
|
|
@@ -55590,8 +55641,8 @@ var LlmProviderWidgetSchema = object({
|
|
|
55590
55641
|
/** ConfigUISchema tree passed through untyped on the wire (the
|
|
55591
55642
|
* notification-output `ConfigSchemaPassthrough` precedent at
|
|
55592
55643
|
* notification-output.cap.ts:151); the exported TS type re-tightens it. */
|
|
55593
|
-
var ConfigSchemaPassthrough$
|
|
55594
|
-
|
|
55644
|
+
var ConfigSchemaPassthrough$1 = unknown();
|
|
55645
|
+
object({
|
|
55595
55646
|
kind: LlmProfileKindSchema,
|
|
55596
55647
|
label: string(),
|
|
55597
55648
|
/** A generic icon name (`cloud`, `plug`, `cpu`) — never a glyph or a logo. */
|
|
@@ -55599,9 +55650,9 @@ var LlmProfileKindDescriptorSchema = object({
|
|
|
55599
55650
|
/** Stamped by each provider so the concat-fanned catalog stays routable. */
|
|
55600
55651
|
addonId: string(),
|
|
55601
55652
|
/** The provider's own fields (`providerConfig`); `password` fields are keychain-held. */
|
|
55602
|
-
connectionSchema: ConfigSchemaPassthrough$
|
|
55653
|
+
connectionSchema: ConfigSchemaPassthrough$1,
|
|
55603
55654
|
/** Provider fields that belong under "Advanced" (extra headers, runtime tuning). */
|
|
55604
|
-
advancedSchema: ConfigSchemaPassthrough$
|
|
55655
|
+
advancedSchema: ConfigSchemaPassthrough$1.optional(),
|
|
55605
55656
|
/** The kind's defaults; a model from `listModels` may override any of them. */
|
|
55606
55657
|
features: LlmModelFeaturesSchema,
|
|
55607
55658
|
/** `false` = the provider stamps `supportsVision` itself (from what a node reports). */
|
|
@@ -55730,20 +55781,7 @@ method(LlmGenerateBaseInputSchema, LlmGenerateResultSchema, {
|
|
|
55730
55781
|
}), method(object({
|
|
55731
55782
|
addonId: string().optional(),
|
|
55732
55783
|
requestId: string()
|
|
55733
|
-
}), _void(), { kind: "mutation" }), method(
|
|
55734
|
-
kind: "mutation",
|
|
55735
|
-
auth: "admin"
|
|
55736
|
-
}), method(RawSecretMigrationInputSchema, RawSecretMigrationReportSchema, {
|
|
55737
|
-
kind: "mutation",
|
|
55738
|
-
auth: "admin"
|
|
55739
|
-
}), method(ProfileRefInputSchema, _void(), {
|
|
55740
|
-
kind: "mutation",
|
|
55741
|
-
auth: "admin"
|
|
55742
|
-
}), method(ProfileRefInputSchema, LlmGenerateResultSchema, {
|
|
55743
|
-
kind: "mutation",
|
|
55744
|
-
auth: "admin",
|
|
55745
|
-
timeoutMs: AI_CAP_TRANSPORT_TIMEOUT_MS
|
|
55746
|
-
}), method(LlmListModelsInputSchema, LlmListModelsResultSchema, {
|
|
55784
|
+
}), _void(), { kind: "mutation" }), method(LlmListModelsInputSchema, LlmListModelsResultSchema, {
|
|
55747
55785
|
kind: "mutation",
|
|
55748
55786
|
auth: "admin"
|
|
55749
55787
|
}), method(object({}), array(LlmDefaultSchema)), method(object({
|
|
@@ -56738,20 +56776,7 @@ method(_void(), MeshStatusSchema), method(object({
|
|
|
56738
56776
|
/** Optional hostname override the host should advertise once joined. */
|
|
56739
56777
|
hostname: string().optional() }), object({
|
|
56740
56778
|
/** Authentication URL the operator should open in a browser. */
|
|
56741
|
-
loginUrl: string() }), { kind: "mutation" }), method(_void(), object({ left: literal(true) }), { kind: "mutation" }), method(_void(), object({ loggedOut: literal(true) }), { kind: "mutation" }), method(_void(), object({ peers: array(MeshPeerSchema).readonly() }))
|
|
56742
|
-
/** Optional auth key — when provided, probes the key validity
|
|
56743
|
-
* against the provider's API. Omit when already joined to
|
|
56744
|
-
* just ping the daemon. */
|
|
56745
|
-
authKey: string().optional() }), object({
|
|
56746
|
-
ok: boolean(),
|
|
56747
|
-
/** Provider-side identifier resolved by the probe (tailnet
|
|
56748
|
-
* name for Tailscale, network id for ZeroTier, etc.). */
|
|
56749
|
-
tenant: string().optional(),
|
|
56750
|
-
/** Daemon binary version, when reachable. */
|
|
56751
|
-
daemonVersion: string().optional(),
|
|
56752
|
-
/** Human-readable error when `ok: false`. */
|
|
56753
|
-
error: string().optional()
|
|
56754
|
-
}), { kind: "mutation" });
|
|
56779
|
+
loginUrl: string() }), { kind: "mutation" }), method(_void(), object({ left: literal(true) }), { kind: "mutation" }), method(_void(), object({ loggedOut: literal(true) }), { kind: "mutation" }), method(_void(), object({ peers: array(MeshPeerSchema).readonly() }));
|
|
56755
56780
|
object({
|
|
56756
56781
|
value: number().nullable().meta(readingMeta("device", { label: "Total" })),
|
|
56757
56782
|
lastFetchedAt: number()
|
|
@@ -66409,61 +66434,6 @@ var OauthIntegrationDescriptorSchema = object({
|
|
|
66409
66434
|
});
|
|
66410
66435
|
method(_void(), OauthIntegrationDescriptorSchema, { auth: "admin" });
|
|
66411
66436
|
/**
|
|
66412
|
-
* `oidc-connections` — CRUD for IdP rows under `auth-oidc` (D828 sibling).
|
|
66413
|
-
*
|
|
66414
|
-
* One hub-only singleton (`auth-oidc`). The Settings UI is a DataTable +
|
|
66415
|
-
* ItemFormDialog; there is no `editable-array` in `globalSettingsSchema`.
|
|
66416
|
-
* Operator-chosen `id` is the URL slug (`/addon/auth-oidc/<id>/…`) and is
|
|
66417
|
-
* immutable after create.
|
|
66418
|
-
*/
|
|
66419
|
-
var ConfigSchemaPassthrough$1 = unknown();
|
|
66420
|
-
var OidcConnectionSchema = object({
|
|
66421
|
-
id: string().min(1),
|
|
66422
|
-
displayName: string(),
|
|
66423
|
-
icon: string(),
|
|
66424
|
-
issuerUrl: string(),
|
|
66425
|
-
clientId: string(),
|
|
66426
|
-
/** Client id + secret present — never the secret value. */
|
|
66427
|
-
configured: boolean(),
|
|
66428
|
-
/**
|
|
66429
|
-
* Values for the connection form schema. `clientSecret` is a keychain
|
|
66430
|
-
* {@link SecretFieldView} or `null` / `''`, never plaintext (D789).
|
|
66431
|
-
*/
|
|
66432
|
-
formValues: record(string(), unknown()).optional()
|
|
66433
|
-
});
|
|
66434
|
-
/**
|
|
66435
|
-
* Patch one connection. Omitted or empty `clientSecret` keeps the stored
|
|
66436
|
-
* secret (D787). `{ $secretClear: true }` clears it. `id` is never renamed.
|
|
66437
|
-
*/
|
|
66438
|
-
var OidcConnectionUpdateInputSchema = object({
|
|
66439
|
-
id: string().min(1),
|
|
66440
|
-
displayName: string().min(1),
|
|
66441
|
-
icon: string().optional(),
|
|
66442
|
-
issuerUrl: string().optional(),
|
|
66443
|
-
clientId: string().optional(),
|
|
66444
|
-
clientSecret: union([string(), object({ $secretClear: literal(true) })]).optional(),
|
|
66445
|
-
redirectUri: string().optional(),
|
|
66446
|
-
scopes: string().optional(),
|
|
66447
|
-
usernameClaim: _enum([
|
|
66448
|
-
"preferred_username",
|
|
66449
|
-
"email",
|
|
66450
|
-
"sub"
|
|
66451
|
-
]).optional()
|
|
66452
|
-
});
|
|
66453
|
-
method(object({}), ConfigSchemaPassthrough$1, { auth: "admin" }), method(object({}), array(OidcConnectionSchema).readonly(), { auth: "admin" }), method(object({
|
|
66454
|
-
id: string().min(1).regex(/^[a-z0-9][a-z0-9-]{0,63}$/, "id must be a lowercase slug"),
|
|
66455
|
-
displayName: string().min(1)
|
|
66456
|
-
}), object({ id: string() }), {
|
|
66457
|
-
kind: "mutation",
|
|
66458
|
-
auth: "admin"
|
|
66459
|
-
}), method(OidcConnectionUpdateInputSchema, object({ ok: literal(true) }), {
|
|
66460
|
-
kind: "mutation",
|
|
66461
|
-
auth: "admin"
|
|
66462
|
-
}), method(object({ id: string().min(1) }), object({ ok: literal(true) }), {
|
|
66463
|
-
kind: "mutation",
|
|
66464
|
-
auth: "admin"
|
|
66465
|
-
});
|
|
66466
|
-
/**
|
|
66467
66437
|
* Generic on-screen-display (video overlay) cap. Each camera exposes
|
|
66468
66438
|
* a firmware-defined list of overlays (channel name, timestamp,
|
|
66469
66439
|
* watermark, logo, …); the cap models them as a homogeneous array of
|
|
@@ -72636,21 +72606,6 @@ method(object({
|
|
|
72636
72606
|
*/
|
|
72637
72607
|
ttlSec: union([number().int().positive(), literal("never")]).optional()
|
|
72638
72608
|
}), object({ token: string() }), { auth: "admin" }), method(object({ token: string() }), SsoBridgeClaimsSchema.nullable(), { auth: "admin" });
|
|
72639
|
-
var ProviderListEntrySchema = discriminatedUnion("shouldSaveDiskSpace", [object({
|
|
72640
|
-
providerId: string().min(1),
|
|
72641
|
-
displayName: string().min(1),
|
|
72642
|
-
configSchema: unknown(),
|
|
72643
|
-
nodeLocal: boolean(),
|
|
72644
|
-
shouldSaveDiskSpace: literal(true),
|
|
72645
|
-
minFreePercent: number().min(0).max(100)
|
|
72646
|
-
}), object({
|
|
72647
|
-
providerId: string().min(1),
|
|
72648
|
-
displayName: string().min(1),
|
|
72649
|
-
configSchema: unknown(),
|
|
72650
|
-
nodeLocal: boolean(),
|
|
72651
|
-
shouldSaveDiskSpace: literal(false),
|
|
72652
|
-
minFreePercent: literal(null)
|
|
72653
|
-
})]);
|
|
72654
72609
|
method(object({
|
|
72655
72610
|
location: StorageLocationRefSchema,
|
|
72656
72611
|
relativePath: string()
|
|
@@ -72712,42 +72667,7 @@ method(object({
|
|
|
72712
72667
|
}), _instanceof(Uint8Array), { auth: "admin" }), method(object({ downloadId: string() }), _void(), {
|
|
72713
72668
|
kind: "mutation",
|
|
72714
72669
|
auth: "admin"
|
|
72715
|
-
}), method(object({ type: StorageLocationTypeSchema.optional() }), array(StorageLocationSchema).readonly()), method(_void(), array(StorageLocationDeclarationSchema).readonly()), method(
|
|
72716
|
-
createdAt: true,
|
|
72717
|
-
updatedAt: true
|
|
72718
|
-
}), StorageLocationSchema, {
|
|
72719
|
-
kind: "mutation",
|
|
72720
|
-
auth: "admin"
|
|
72721
|
-
}), method(object({
|
|
72722
|
-
id: string(),
|
|
72723
|
-
force: boolean().optional()
|
|
72724
|
-
}), _void(), {
|
|
72725
|
-
kind: "mutation",
|
|
72726
|
-
auth: "admin"
|
|
72727
|
-
}), method(_void(), array(StorageDrainProgressSchema).readonly()), method(object({ id: string() }), object({
|
|
72728
|
-
ok: boolean(),
|
|
72729
|
-
error: string().optional()
|
|
72730
|
-
}), { auth: "admin" }), method(_void(), array(ProviderListEntrySchema).readonly()), method(object({
|
|
72731
|
-
providerId: string(),
|
|
72732
|
-
/**
|
|
72733
|
-
* The location this config is an UNSAVED edit of, when there is one.
|
|
72734
|
-
*
|
|
72735
|
-
* `listLocations` replaces every declared secret with the redaction
|
|
72736
|
-
* sentinel, so the edit modal's form state holds the sentinel for any
|
|
72737
|
-
* credential the operator did not retype — and posting that here
|
|
72738
|
-
* without a way to resolve it makes the provider try to authenticate
|
|
72739
|
-
* as `__camstack_redacted__` and report the operator's own working
|
|
72740
|
-
* password as wrong. Given this id, the orchestrator restores each
|
|
72741
|
-
* sentinel from the stored config (same rule as `upsertLocation`)
|
|
72742
|
-
* before dispatching. Omitted by the "Add location" wizard, where
|
|
72743
|
-
* every value was typed just now and nothing is stored yet.
|
|
72744
|
-
*/
|
|
72745
|
-
locationId: string().optional(),
|
|
72746
|
-
config: record(string(), unknown())
|
|
72747
|
-
}), object({
|
|
72748
|
-
ok: boolean(),
|
|
72749
|
-
error: string().optional()
|
|
72750
|
-
}), { auth: "admin" });
|
|
72670
|
+
}), method(object({ type: StorageLocationTypeSchema.optional() }), array(StorageLocationSchema).readonly()), method(_void(), array(StorageLocationDeclarationSchema).readonly()), method(_void(), array(StorageDrainProgressSchema).readonly());
|
|
72751
72671
|
/** One table of the settings database. */
|
|
72752
72672
|
var StorageTableSchema = object({
|
|
72753
72673
|
/** The table's full name, e.g. `recorder:addon-settings` or `addon-device-settings`. */
|
|
@@ -80340,13 +80260,6 @@ Object.freeze({
|
|
|
80340
80260
|
access: "delete",
|
|
80341
80261
|
role: "admin"
|
|
80342
80262
|
},
|
|
80343
|
-
"llm.deleteProfile": {
|
|
80344
|
-
capName: "llm",
|
|
80345
|
-
capScope: "system",
|
|
80346
|
-
addonId: null,
|
|
80347
|
-
access: "delete",
|
|
80348
|
-
role: "admin"
|
|
80349
|
-
},
|
|
80350
80263
|
"llm.generate": {
|
|
80351
80264
|
capName: "llm",
|
|
80352
80265
|
capScope: "system",
|
|
@@ -80410,20 +80323,6 @@ Object.freeze({
|
|
|
80410
80323
|
access: "view",
|
|
80411
80324
|
role: "admin"
|
|
80412
80325
|
},
|
|
80413
|
-
"llm.listProfileKinds": {
|
|
80414
|
-
capName: "llm",
|
|
80415
|
-
capScope: "system",
|
|
80416
|
-
addonId: null,
|
|
80417
|
-
access: "view",
|
|
80418
|
-
role: "admin"
|
|
80419
|
-
},
|
|
80420
|
-
"llm.listProfiles": {
|
|
80421
|
-
capName: "llm",
|
|
80422
|
-
capScope: "system",
|
|
80423
|
-
addonId: null,
|
|
80424
|
-
access: "view",
|
|
80425
|
-
role: "admin"
|
|
80426
|
-
},
|
|
80427
80326
|
"llm.listRuntimeNodes": {
|
|
80428
80327
|
capName: "llm",
|
|
80429
80328
|
capScope: "system",
|
|
@@ -80431,13 +80330,6 @@ Object.freeze({
|
|
|
80431
80330
|
access: "view",
|
|
80432
80331
|
role: "admin"
|
|
80433
80332
|
},
|
|
80434
|
-
"llm.migrateRawSecrets": {
|
|
80435
|
-
capName: "llm",
|
|
80436
|
-
capScope: "system",
|
|
80437
|
-
addonId: null,
|
|
80438
|
-
access: "create",
|
|
80439
|
-
role: "admin"
|
|
80440
|
-
},
|
|
80441
80333
|
"llm.resolveModelRef": {
|
|
80442
80334
|
capName: "llm",
|
|
80443
80335
|
capScope: "system",
|
|
@@ -80466,20 +80358,6 @@ Object.freeze({
|
|
|
80466
80358
|
access: "create",
|
|
80467
80359
|
role: "admin"
|
|
80468
80360
|
},
|
|
80469
|
-
"llm.testProfile": {
|
|
80470
|
-
capName: "llm",
|
|
80471
|
-
capScope: "system",
|
|
80472
|
-
addonId: null,
|
|
80473
|
-
access: "create",
|
|
80474
|
-
role: "admin"
|
|
80475
|
-
},
|
|
80476
|
-
"llm.upsertProfile": {
|
|
80477
|
-
capName: "llm",
|
|
80478
|
-
capScope: "system",
|
|
80479
|
-
addonId: null,
|
|
80480
|
-
access: "create",
|
|
80481
|
-
role: "admin"
|
|
80482
|
-
},
|
|
80483
80361
|
"llmRuntime.appleAvailability": {
|
|
80484
80362
|
capName: "llm-runtime",
|
|
80485
80363
|
capScope: "system",
|
|
@@ -80984,13 +80862,6 @@ Object.freeze({
|
|
|
80984
80862
|
access: "create",
|
|
80985
80863
|
role: "admin"
|
|
80986
80864
|
},
|
|
80987
|
-
"meshNetwork.testConnection": {
|
|
80988
|
-
capName: "mesh-network",
|
|
80989
|
-
capScope: "system",
|
|
80990
|
-
addonId: null,
|
|
80991
|
-
access: "create",
|
|
80992
|
-
role: "admin"
|
|
80993
|
-
},
|
|
80994
80865
|
"meter.getStatus": {
|
|
80995
80866
|
capName: "meter",
|
|
80996
80867
|
capScope: "device",
|
|
@@ -81649,13 +81520,6 @@ Object.freeze({
|
|
|
81649
81520
|
access: "create",
|
|
81650
81521
|
role: "admin"
|
|
81651
81522
|
},
|
|
81652
|
-
"notificationOutput.deleteTarget": {
|
|
81653
|
-
capName: "notification-output",
|
|
81654
|
-
capScope: "system",
|
|
81655
|
-
addonId: null,
|
|
81656
|
-
access: "delete",
|
|
81657
|
-
role: "admin"
|
|
81658
|
-
},
|
|
81659
81523
|
"notificationOutput.discoverTargets": {
|
|
81660
81524
|
capName: "notification-output",
|
|
81661
81525
|
capScope: "system",
|
|
@@ -81663,13 +81527,6 @@ Object.freeze({
|
|
|
81663
81527
|
access: "view",
|
|
81664
81528
|
role: "admin"
|
|
81665
81529
|
},
|
|
81666
|
-
"notificationOutput.getTarget": {
|
|
81667
|
-
capName: "notification-output",
|
|
81668
|
-
capScope: "system",
|
|
81669
|
-
addonId: null,
|
|
81670
|
-
access: "view",
|
|
81671
|
-
role: "admin"
|
|
81672
|
-
},
|
|
81673
81530
|
"notificationOutput.listTargetKinds": {
|
|
81674
81531
|
capName: "notification-output",
|
|
81675
81532
|
capScope: "system",
|
|
@@ -81677,13 +81534,6 @@ Object.freeze({
|
|
|
81677
81534
|
access: "view",
|
|
81678
81535
|
role: "admin"
|
|
81679
81536
|
},
|
|
81680
|
-
"notificationOutput.listTargets": {
|
|
81681
|
-
capName: "notification-output",
|
|
81682
|
-
capScope: "system",
|
|
81683
|
-
addonId: null,
|
|
81684
|
-
access: "view",
|
|
81685
|
-
role: "admin"
|
|
81686
|
-
},
|
|
81687
81537
|
"notificationOutput.probeTargetConfig": {
|
|
81688
81538
|
capName: "notification-output",
|
|
81689
81539
|
capScope: "system",
|
|
@@ -81698,13 +81548,6 @@ Object.freeze({
|
|
|
81698
81548
|
access: "create",
|
|
81699
81549
|
role: "admin"
|
|
81700
81550
|
},
|
|
81701
|
-
"notificationOutput.setTargetEnabled": {
|
|
81702
|
-
capName: "notification-output",
|
|
81703
|
-
capScope: "system",
|
|
81704
|
-
addonId: null,
|
|
81705
|
-
access: "create",
|
|
81706
|
-
role: "admin"
|
|
81707
|
-
},
|
|
81708
81551
|
"notificationOutput.testTarget": {
|
|
81709
81552
|
capName: "notification-output",
|
|
81710
81553
|
capScope: "system",
|
|
@@ -81712,13 +81555,6 @@ Object.freeze({
|
|
|
81712
81555
|
access: "create",
|
|
81713
81556
|
role: "admin"
|
|
81714
81557
|
},
|
|
81715
|
-
"notificationOutput.upsertTarget": {
|
|
81716
|
-
capName: "notification-output",
|
|
81717
|
-
capScope: "system",
|
|
81718
|
-
addonId: null,
|
|
81719
|
-
access: "create",
|
|
81720
|
-
role: "admin"
|
|
81721
|
-
},
|
|
81722
81558
|
"notificationRules.activateAlarmProfile": {
|
|
81723
81559
|
capName: "notification-rules",
|
|
81724
81560
|
capScope: "system",
|
|
@@ -81978,41 +81814,6 @@ Object.freeze({
|
|
|
81978
81814
|
access: "view",
|
|
81979
81815
|
role: "admin"
|
|
81980
81816
|
},
|
|
81981
|
-
"oidcConnections.connectionFormSchema": {
|
|
81982
|
-
capName: "oidc-connections",
|
|
81983
|
-
capScope: "system",
|
|
81984
|
-
addonId: null,
|
|
81985
|
-
access: "view",
|
|
81986
|
-
role: "admin"
|
|
81987
|
-
},
|
|
81988
|
-
"oidcConnections.createConnection": {
|
|
81989
|
-
capName: "oidc-connections",
|
|
81990
|
-
capScope: "system",
|
|
81991
|
-
addonId: null,
|
|
81992
|
-
access: "create",
|
|
81993
|
-
role: "admin"
|
|
81994
|
-
},
|
|
81995
|
-
"oidcConnections.deleteConnection": {
|
|
81996
|
-
capName: "oidc-connections",
|
|
81997
|
-
capScope: "system",
|
|
81998
|
-
addonId: null,
|
|
81999
|
-
access: "delete",
|
|
82000
|
-
role: "admin"
|
|
82001
|
-
},
|
|
82002
|
-
"oidcConnections.listConnections": {
|
|
82003
|
-
capName: "oidc-connections",
|
|
82004
|
-
capScope: "system",
|
|
82005
|
-
addonId: null,
|
|
82006
|
-
access: "view",
|
|
82007
|
-
role: "admin"
|
|
82008
|
-
},
|
|
82009
|
-
"oidcConnections.updateConnection": {
|
|
82010
|
-
capName: "oidc-connections",
|
|
82011
|
-
capScope: "system",
|
|
82012
|
-
addonId: null,
|
|
82013
|
-
access: "create",
|
|
82014
|
-
role: "admin"
|
|
82015
|
-
},
|
|
82016
81817
|
"osd.getStatus": {
|
|
82017
81818
|
capName: "osd",
|
|
82018
81819
|
capScope: "device",
|
|
@@ -85044,13 +84845,6 @@ Object.freeze({
|
|
|
85044
84845
|
access: "delete",
|
|
85045
84846
|
role: "admin"
|
|
85046
84847
|
},
|
|
85047
|
-
"storage.deleteLocation": {
|
|
85048
|
-
capName: "storage",
|
|
85049
|
-
capScope: "system",
|
|
85050
|
-
addonId: null,
|
|
85051
|
-
access: "delete",
|
|
85052
|
-
role: "admin"
|
|
85053
|
-
},
|
|
85054
84848
|
"storage.endDownload": {
|
|
85055
84849
|
capName: "storage",
|
|
85056
84850
|
capScope: "system",
|
|
@@ -85107,13 +84901,6 @@ Object.freeze({
|
|
|
85107
84901
|
access: "view",
|
|
85108
84902
|
role: "admin"
|
|
85109
84903
|
},
|
|
85110
|
-
"storage.listProviders": {
|
|
85111
|
-
capName: "storage",
|
|
85112
|
-
capScope: "system",
|
|
85113
|
-
addonId: null,
|
|
85114
|
-
access: "view",
|
|
85115
|
-
role: "admin"
|
|
85116
|
-
},
|
|
85117
84904
|
"storage.read": {
|
|
85118
84905
|
capName: "storage",
|
|
85119
84906
|
capScope: "system",
|
|
@@ -85135,27 +84922,6 @@ Object.freeze({
|
|
|
85135
84922
|
access: "view",
|
|
85136
84923
|
role: "admin"
|
|
85137
84924
|
},
|
|
85138
|
-
"storage.testConfig": {
|
|
85139
|
-
capName: "storage",
|
|
85140
|
-
capScope: "system",
|
|
85141
|
-
addonId: null,
|
|
85142
|
-
access: "view",
|
|
85143
|
-
role: "admin"
|
|
85144
|
-
},
|
|
85145
|
-
"storage.testLocation": {
|
|
85146
|
-
capName: "storage",
|
|
85147
|
-
capScope: "system",
|
|
85148
|
-
addonId: null,
|
|
85149
|
-
access: "view",
|
|
85150
|
-
role: "admin"
|
|
85151
|
-
},
|
|
85152
|
-
"storage.upsertLocation": {
|
|
85153
|
-
capName: "storage",
|
|
85154
|
-
capScope: "system",
|
|
85155
|
-
addonId: null,
|
|
85156
|
-
access: "create",
|
|
85157
|
-
role: "admin"
|
|
85158
|
-
},
|
|
85159
84925
|
"storage.write": {
|
|
85160
84926
|
capName: "storage",
|
|
85161
84927
|
capScope: "system",
|
|
@@ -91308,20 +91074,6 @@ object({
|
|
|
91308
91074
|
});
|
|
91309
91075
|
//#endregion
|
|
91310
91076
|
//#region src/custom-actions.ts
|
|
91311
|
-
var SourceIdSchema = string().min(1).regex(/^[a-z0-9][a-z0-9-]{0,63}$/, "id must be a lowercase slug");
|
|
91312
|
-
/**
|
|
91313
|
-
* A source as the admin list shows it. No `args`: a credential never leaves the
|
|
91314
|
-
* addon - `formValues` carries the redaction token where one is stored (D880).
|
|
91315
|
-
*/
|
|
91316
|
-
var WasteSourceRowSchema = object({
|
|
91317
|
-
id: string(),
|
|
91318
|
-
label: string(),
|
|
91319
|
-
module: string(),
|
|
91320
|
-
enabled: boolean(),
|
|
91321
|
-
formValues: record(string(), unknown()).optional(),
|
|
91322
|
-
serviceId: string().optional(),
|
|
91323
|
-
countryId: string().optional()
|
|
91324
|
-
});
|
|
91325
91077
|
var wasteCollectionsActions = defineCustomActions({
|
|
91326
91078
|
fetchNow: customAction(object({
|
|
91327
91079
|
/** When set, refresh only this configured source id; otherwise all enabled. */
|
|
@@ -91336,10 +91088,6 @@ sourceId: string().min(1).optional() }).optional(), object({
|
|
|
91336
91088
|
kind: "mutation",
|
|
91337
91089
|
auth: "admin"
|
|
91338
91090
|
}),
|
|
91339
|
-
listSources: customAction(object({}), array(WasteSourceRowSchema).readonly(), {
|
|
91340
|
-
kind: "mutation",
|
|
91341
|
-
auth: "admin"
|
|
91342
|
-
}),
|
|
91343
91091
|
listCountries: customAction(object({}), array(object({
|
|
91344
91092
|
id: string(),
|
|
91345
91093
|
label: string()
|
|
@@ -91357,43 +91105,6 @@ sourceId: string().min(1).optional() }).optional(), object({
|
|
|
91357
91105
|
})).readonly(), {
|
|
91358
91106
|
kind: "mutation",
|
|
91359
91107
|
auth: "admin"
|
|
91360
|
-
}),
|
|
91361
|
-
serviceFormSchema: customAction(object({ serviceId: string().min(1) }), unknown(), {
|
|
91362
|
-
kind: "mutation",
|
|
91363
|
-
auth: "admin"
|
|
91364
|
-
}),
|
|
91365
|
-
createSource: customAction(object({
|
|
91366
|
-
/** Optional override; when omitted the id is derived from `label`. */
|
|
91367
|
-
id: SourceIdSchema.optional(),
|
|
91368
|
-
label: string().min(1),
|
|
91369
|
-
serviceId: string().min(1),
|
|
91370
|
-
args: record(string(), unknown()).default({}),
|
|
91371
|
-
enabled: boolean().optional()
|
|
91372
|
-
}), object({ id: string() }), {
|
|
91373
|
-
kind: "mutation",
|
|
91374
|
-
auth: "admin"
|
|
91375
|
-
}),
|
|
91376
|
-
updateSource: customAction(object({
|
|
91377
|
-
id: string().min(1),
|
|
91378
|
-
label: string().min(1),
|
|
91379
|
-
serviceId: string().min(1).optional(),
|
|
91380
|
-
args: record(string(), unknown()).optional(),
|
|
91381
|
-
enabled: boolean().optional()
|
|
91382
|
-
}), object({ ok: literal(true) }), {
|
|
91383
|
-
kind: "mutation",
|
|
91384
|
-
auth: "admin"
|
|
91385
|
-
}),
|
|
91386
|
-
/**
|
|
91387
|
-
* D880: rewrite every legacy source (free `argsJson`) as typed `args`, moving its
|
|
91388
|
-
* credentials into the keychain. Dry run unless `apply`; idempotent; counts only.
|
|
91389
|
-
*/
|
|
91390
|
-
migrateSources: customAction(RawSecretMigrationInputSchema, RawSecretMigrationReportSchema, {
|
|
91391
|
-
kind: "mutation",
|
|
91392
|
-
auth: "admin"
|
|
91393
|
-
}),
|
|
91394
|
-
deleteSource: customAction(object({ id: string().min(1) }), object({ ok: literal(true) }), {
|
|
91395
|
-
kind: "mutation",
|
|
91396
|
-
auth: "admin"
|
|
91397
91108
|
})
|
|
91398
91109
|
});
|
|
91399
91110
|
//#endregion
|
|
@@ -91427,33 +91138,6 @@ var WASTE_SECRET_STATE_PATHS = SECRET_ARG_KEYS.map((key) => `sources[id].args.${
|
|
|
91427
91138
|
function isSecretArg(key) {
|
|
91428
91139
|
return SECRET_ARG_KEYS.includes(key.toLowerCase());
|
|
91429
91140
|
}
|
|
91430
|
-
/** Is a credential stored for this argument (a value, or a marker the keychain could not serve)? */
|
|
91431
|
-
function isSet(value) {
|
|
91432
|
-
return isSecretMarker(value) || typeof value === "string" && value.length > 0;
|
|
91433
|
-
}
|
|
91434
|
-
/** The arguments as a read surface may show them: a stored credential is the redaction token. */
|
|
91435
|
-
function redactArgs(args) {
|
|
91436
|
-
const out = {};
|
|
91437
|
-
for (const [key, value] of Object.entries(args)) out[key] = isSecretArg(key) && isSet(value) ? REDACTED_SECRET : value;
|
|
91438
|
-
return out;
|
|
91439
|
-
}
|
|
91440
|
-
/**
|
|
91441
|
-
* The write-back of {@link redactArgs}: a credential sent back as the redaction
|
|
91442
|
-
* token means "keep what is stored", so it takes the stored (resolved) value; one
|
|
91443
|
-
* with nothing stored is dropped, never saved as the token.
|
|
91444
|
-
*/
|
|
91445
|
-
function restoreRedactedArgs(incoming, stored) {
|
|
91446
|
-
const out = {};
|
|
91447
|
-
for (const [key, value] of Object.entries(incoming)) {
|
|
91448
|
-
if (isSecretArg(key) && value === "__camstack_redacted__") {
|
|
91449
|
-
const previous = stored[key];
|
|
91450
|
-
if (previous !== void 0) out[key] = previous;
|
|
91451
|
-
continue;
|
|
91452
|
-
}
|
|
91453
|
-
out[key] = value;
|
|
91454
|
-
}
|
|
91455
|
-
return out;
|
|
91456
|
-
}
|
|
91457
91141
|
/**
|
|
91458
91142
|
* Does this argument bag hold a credential that is NOT a usable value: a MARKER the keychain did
|
|
91459
91143
|
* not resolve, or the redaction token (a row read without its credential)? Either would reach a
|
|
@@ -91462,10 +91146,6 @@ function restoreRedactedArgs(incoming, stored) {
|
|
|
91462
91146
|
function hasUnresolvedCredential(args) {
|
|
91463
91147
|
return Object.entries(args).some(([key, value]) => isSecretArg(key) && (isSecretMarker(value) || value === "__camstack_redacted__"));
|
|
91464
91148
|
}
|
|
91465
|
-
/** Does it hold a non-empty credential (any form)? */
|
|
91466
|
-
function hasCredential(args) {
|
|
91467
|
-
return Object.entries(args).some(([key, value]) => isSecretArg(key) && isSet(value));
|
|
91468
|
-
}
|
|
91469
91149
|
//#endregion
|
|
91470
91150
|
//#region src/waste-types.ts
|
|
91471
91151
|
/**
|
|
@@ -91930,35 +91610,6 @@ function reportFailed(log, failures) {
|
|
|
91930
91610
|
};
|
|
91931
91611
|
}
|
|
91932
91612
|
//#endregion
|
|
91933
|
-
//#region src/source-id.ts
|
|
91934
|
-
/**
|
|
91935
|
-
* Stable calendar source ids derived from the operator label.
|
|
91936
|
-
* Pattern: `^[a-z0-9][a-z0-9-]{0,63}$` (max 64 chars).
|
|
91937
|
-
*/
|
|
91938
|
-
var SOURCE_ID_PATTERN = /^[a-z0-9][a-z0-9-]{0,63}$/;
|
|
91939
|
-
/** Slugify a display label into a candidate source id (may be empty). */
|
|
91940
|
-
function slugifySourceLabel(label) {
|
|
91941
|
-
const s = label.normalize("NFKD").replace(/[\u0300-\u036f]/g, "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
91942
|
-
if (s.length === 0) return "";
|
|
91943
|
-
return s.slice(0, 64);
|
|
91944
|
-
}
|
|
91945
|
-
/**
|
|
91946
|
-
* Pick an unused id from `label`. Collisions append `-2`, `-3`, …
|
|
91947
|
-
* (truncated so the result stays within the 64-char pattern).
|
|
91948
|
-
*/
|
|
91949
|
-
function allocateSourceId(label, existingIds) {
|
|
91950
|
-
const taken = existingIds instanceof Set ? existingIds : new Set(existingIds);
|
|
91951
|
-
const baseRaw = slugifySourceLabel(label);
|
|
91952
|
-
const base = baseRaw.length > 0 ? baseRaw : "source";
|
|
91953
|
-
if (!taken.has(base) && SOURCE_ID_PATTERN.test(base)) return base;
|
|
91954
|
-
for (let n = 2; n < 1e4; n++) {
|
|
91955
|
-
const suffix = `-${n}`;
|
|
91956
|
-
const candidate = `${base.slice(0, Math.max(1, 64 - suffix.length)).replace(/-+$/g, "")}${suffix}`;
|
|
91957
|
-
if (SOURCE_ID_PATTERN.test(candidate) && !taken.has(candidate)) return candidate;
|
|
91958
|
-
}
|
|
91959
|
-
throw new Error("could not allocate a unique source id");
|
|
91960
|
-
}
|
|
91961
|
-
//#endregion
|
|
91962
91613
|
//#region src/waste-calendar-source.ts
|
|
91963
91614
|
function startOfUtcDayMs(isoDate) {
|
|
91964
91615
|
const [y, m, d] = isoDate.split("-").map((p) => Number(p));
|
|
@@ -178003,6 +177654,35 @@ function mergeServiceArgs(service, formValues) {
|
|
|
178003
177654
|
return out;
|
|
178004
177655
|
}
|
|
178005
177656
|
//#endregion
|
|
177657
|
+
//#region src/source-id.ts
|
|
177658
|
+
/**
|
|
177659
|
+
* Stable calendar source ids derived from the operator label.
|
|
177660
|
+
* Pattern: `^[a-z0-9][a-z0-9-]{0,63}$` (max 64 chars).
|
|
177661
|
+
*/
|
|
177662
|
+
var SOURCE_ID_PATTERN = /^[a-z0-9][a-z0-9-]{0,63}$/;
|
|
177663
|
+
/** Slugify a display label into a candidate source id (may be empty). */
|
|
177664
|
+
function slugifySourceLabel(label) {
|
|
177665
|
+
const s = label.normalize("NFKD").replace(/[\u0300-\u036f]/g, "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
177666
|
+
if (s.length === 0) return "";
|
|
177667
|
+
return s.slice(0, 64);
|
|
177668
|
+
}
|
|
177669
|
+
/**
|
|
177670
|
+
* Pick an unused id from `label`. Collisions append `-2`, `-3`, …
|
|
177671
|
+
* (truncated so the result stays within the 64-char pattern).
|
|
177672
|
+
*/
|
|
177673
|
+
function allocateSourceId(label, existingIds) {
|
|
177674
|
+
const taken = existingIds instanceof Set ? existingIds : new Set(existingIds);
|
|
177675
|
+
const baseRaw = slugifySourceLabel(label);
|
|
177676
|
+
const base = baseRaw.length > 0 ? baseRaw : "source";
|
|
177677
|
+
if (!taken.has(base) && SOURCE_ID_PATTERN.test(base)) return base;
|
|
177678
|
+
for (let n = 2; n < 1e4; n++) {
|
|
177679
|
+
const suffix = `-${n}`;
|
|
177680
|
+
const candidate = `${base.slice(0, Math.max(1, 64 - suffix.length)).replace(/-+$/g, "")}${suffix}`;
|
|
177681
|
+
if (SOURCE_ID_PATTERN.test(candidate) && !taken.has(candidate)) return candidate;
|
|
177682
|
+
}
|
|
177683
|
+
throw new Error("could not allocate a unique source id");
|
|
177684
|
+
}
|
|
177685
|
+
//#endregion
|
|
178006
177686
|
//#region src/waste-source-store.ts
|
|
178007
177687
|
/**
|
|
178008
177688
|
* A stored body as a source. A row read while the keychain is locked still carries its markers
|
|
@@ -178506,23 +178186,13 @@ var WasteCollectionsAddon = class extends BaseAddon {
|
|
|
178506
178186
|
customActions: wasteCollectionsActions,
|
|
178507
178187
|
actionHandlers: {
|
|
178508
178188
|
fetchNow: async (input) => this.fetchNow(input),
|
|
178509
|
-
listSources: async () => this.listSources(),
|
|
178510
178189
|
listCountries: async () => listCatalogCountries(),
|
|
178511
178190
|
listServices: async (input) => listCatalogServices(input.countryId).map((s) => ({
|
|
178512
178191
|
id: s.id,
|
|
178513
178192
|
label: serviceDisplayLabel(s),
|
|
178514
178193
|
title: s.label,
|
|
178515
178194
|
module: s.module
|
|
178516
|
-
}))
|
|
178517
|
-
serviceFormSchema: async (input) => {
|
|
178518
|
-
const service = findCatalogService(input.serviceId);
|
|
178519
|
-
if (!service) throw new Error(`unknown service '${input.serviceId}'`);
|
|
178520
|
-
return serviceFormSchema(service);
|
|
178521
|
-
},
|
|
178522
|
-
createSource: async (input) => this.createSource(input),
|
|
178523
|
-
updateSource: async (input) => this.updateSource(input),
|
|
178524
|
-
deleteSource: async (input) => this.deleteSource(input),
|
|
178525
|
-
migrateSources: async (input) => this.migrateSources(input.apply)
|
|
178195
|
+
}))
|
|
178526
178196
|
}
|
|
178527
178197
|
};
|
|
178528
178198
|
}
|
|
@@ -178589,17 +178259,6 @@ var WasteCollectionsAddon = class extends BaseAddon {
|
|
|
178589
178259
|
if (!this.legacyServed) return records;
|
|
178590
178260
|
return withUnmigratedLegacyRows(records, this.parseConfigSources(this.config.sources));
|
|
178591
178261
|
}
|
|
178592
|
-
async persistSources(sources) {
|
|
178593
|
-
this.activeSources = [...sources];
|
|
178594
|
-
const store = this.sourceStore;
|
|
178595
|
-
if (store !== null) {
|
|
178596
|
-
await store.replaceAll(sources);
|
|
178597
|
-
const kept = new Set(sources.map((s) => s.id));
|
|
178598
|
-
await this.pruneLegacyRows((legacyId) => kept.has(legacyId));
|
|
178599
|
-
return;
|
|
178600
|
-
}
|
|
178601
|
-
await this.updateGlobalSettings({ sources: [...sources] });
|
|
178602
|
-
}
|
|
178603
178262
|
/**
|
|
178604
178263
|
* A source deleted at the records leaves the legacy setting too, or it would reappear beside the
|
|
178605
178264
|
* records (while the migration is pending) or be copied back by it.
|
|
@@ -178614,195 +178273,6 @@ var WasteCollectionsAddon = class extends BaseAddon {
|
|
|
178614
178273
|
if (this.activeSources.length > 0 || this.sourceStore !== null) return this.activeSources;
|
|
178615
178274
|
return this.parseConfigSources(this.config.sources);
|
|
178616
178275
|
}
|
|
178617
|
-
listSources() {
|
|
178618
|
-
return this.configuredSources().map((s) => {
|
|
178619
|
-
let args = {};
|
|
178620
|
-
try {
|
|
178621
|
-
args = sourceArgs(s);
|
|
178622
|
-
} catch {
|
|
178623
|
-
args = { ...s.args };
|
|
178624
|
-
}
|
|
178625
|
-
const matched = matchCatalogService(s.module, args);
|
|
178626
|
-
const formValues = {
|
|
178627
|
-
label: s.label,
|
|
178628
|
-
enabled: s.enabled,
|
|
178629
|
-
...redactArgs(args)
|
|
178630
|
-
};
|
|
178631
|
-
if (matched) for (const key of Object.keys(matched.defaultArgs)) delete formValues[key];
|
|
178632
|
-
return {
|
|
178633
|
-
id: s.id,
|
|
178634
|
-
label: s.label,
|
|
178635
|
-
module: s.module,
|
|
178636
|
-
enabled: s.enabled,
|
|
178637
|
-
formValues,
|
|
178638
|
-
serviceId: matched?.id,
|
|
178639
|
-
countryId: matched?.countryId
|
|
178640
|
-
};
|
|
178641
|
-
});
|
|
178642
|
-
}
|
|
178643
|
-
async createSource(input) {
|
|
178644
|
-
const existing = this.configuredSources().map((s) => s.id);
|
|
178645
|
-
let id;
|
|
178646
|
-
if (input.id !== void 0) {
|
|
178647
|
-
if (!SOURCE_ID_PATTERN.test(input.id)) throw new Error("id must be a lowercase slug (e.g. ics-home, junker)");
|
|
178648
|
-
if (existing.includes(input.id)) throw new Error(`source '${input.id}' already exists`);
|
|
178649
|
-
id = input.id;
|
|
178650
|
-
} else id = allocateSourceId(input.label, existing);
|
|
178651
|
-
const service = findCatalogService(input.serviceId);
|
|
178652
|
-
if (!service) throw new Error(`unknown service '${input.serviceId}'`);
|
|
178653
|
-
const row = WasteSourceConfigSchema.parse({
|
|
178654
|
-
id,
|
|
178655
|
-
label: input.label,
|
|
178656
|
-
module: service.module,
|
|
178657
|
-
args: this.typedArgs(mergeServiceArgs(service, restoreRedactedArgs(input.args, {}))),
|
|
178658
|
-
enabled: input.enabled ?? true
|
|
178659
|
-
});
|
|
178660
|
-
await this.persistSources([...this.configuredSources(), row]);
|
|
178661
|
-
return { id };
|
|
178662
|
-
}
|
|
178663
|
-
async updateSource(input) {
|
|
178664
|
-
const sources = this.configuredSources();
|
|
178665
|
-
const idx = sources.findIndex((s) => s.id === input.id);
|
|
178666
|
-
if (idx < 0) throw new Error(`source '${input.id}' not found`);
|
|
178667
|
-
const prev = sources[idx];
|
|
178668
|
-
let prevArgs = {};
|
|
178669
|
-
try {
|
|
178670
|
-
prevArgs = sourceArgs(prev);
|
|
178671
|
-
} catch {
|
|
178672
|
-
prevArgs = { ...prev.args };
|
|
178673
|
-
}
|
|
178674
|
-
let module = prev.module;
|
|
178675
|
-
let args = prevArgs;
|
|
178676
|
-
if (input.serviceId !== void 0) {
|
|
178677
|
-
const service = findCatalogService(input.serviceId);
|
|
178678
|
-
if (!service) throw new Error(`unknown service '${input.serviceId}'`);
|
|
178679
|
-
module = service.module;
|
|
178680
|
-
args = mergeServiceArgs(service, restoreRedactedArgs(input.args ?? {}, prevArgs));
|
|
178681
|
-
} else if (input.args !== void 0) {
|
|
178682
|
-
const sent = restoreRedactedArgs(input.args, prevArgs);
|
|
178683
|
-
const matched = matchCatalogService(prev.module, prevArgs);
|
|
178684
|
-
args = matched ? mergeServiceArgs(matched, sent) : {
|
|
178685
|
-
...prevArgs,
|
|
178686
|
-
...sent
|
|
178687
|
-
};
|
|
178688
|
-
}
|
|
178689
|
-
const next = WasteSourceConfigSchema.parse({
|
|
178690
|
-
id: prev.id,
|
|
178691
|
-
label: input.label,
|
|
178692
|
-
module,
|
|
178693
|
-
args: this.typedArgs(args),
|
|
178694
|
-
enabled: input.enabled ?? prev.enabled
|
|
178695
|
-
});
|
|
178696
|
-
await this.persistSources(sources.map((s, i) => i === idx ? next : s));
|
|
178697
|
-
return { ok: true };
|
|
178698
|
-
}
|
|
178699
|
-
/** Validate an argument bag against the typed value schema (a nested object is refused). */
|
|
178700
|
-
typedArgs(args) {
|
|
178701
|
-
const out = {};
|
|
178702
|
-
for (const [key, value] of Object.entries(args)) out[key] = WasteArgValueSchema.parse(value);
|
|
178703
|
-
return out;
|
|
178704
|
-
}
|
|
178705
|
-
/**
|
|
178706
|
-
* D880: rewrite every legacy source (free `argsJson`) as typed `args`; its
|
|
178707
|
-
* credentials go to the keychain through the normal save path. Then every
|
|
178708
|
-
* credential is read back and compared with what the legacy row held; on any
|
|
178709
|
-
* difference the legacy rows are written back, so nothing is lost. Dry run
|
|
178710
|
-
* unless `apply`. Idempotent. Counts only - never an argument, a ref or an id.
|
|
178711
|
-
*/
|
|
178712
|
-
async migrateSources(apply) {
|
|
178713
|
-
const sources = this.configuredSources();
|
|
178714
|
-
const legacy = sources.filter((s) => s.argsJson !== void 0);
|
|
178715
|
-
const rawRows = legacy.filter((s) => {
|
|
178716
|
-
try {
|
|
178717
|
-
return hasCredential(sourceArgs(s));
|
|
178718
|
-
} catch {
|
|
178719
|
-
return false;
|
|
178720
|
-
}
|
|
178721
|
-
});
|
|
178722
|
-
const markerRows = sources.filter((s) => s.argsJson === void 0 && hasCredential(s.args)).length;
|
|
178723
|
-
const report = (over) => ({
|
|
178724
|
-
apply,
|
|
178725
|
-
rows: sources.length,
|
|
178726
|
-
raw: rawRows.length,
|
|
178727
|
-
markers: markerRows,
|
|
178728
|
-
migrated: 0,
|
|
178729
|
-
failed: 0,
|
|
178730
|
-
remainingRaw: rawRows.length,
|
|
178731
|
-
unresolvedMarkers: this.unresolvedSecrets.length,
|
|
178732
|
-
redundantCopies: legacy.length,
|
|
178733
|
-
verified: rawRows.length === 0 && this.unresolvedSecrets.length === 0,
|
|
178734
|
-
...over
|
|
178735
|
-
});
|
|
178736
|
-
if (!apply || legacy.length === 0) return report({});
|
|
178737
|
-
const expected = /* @__PURE__ */ new Map();
|
|
178738
|
-
const unconverted = /* @__PURE__ */ new Set();
|
|
178739
|
-
const migrated = sources.map((s) => {
|
|
178740
|
-
if (s.argsJson === void 0) return s;
|
|
178741
|
-
try {
|
|
178742
|
-
const args = sourceArgs(s);
|
|
178743
|
-
const row = WasteSourceConfigSchema.parse({
|
|
178744
|
-
id: s.id,
|
|
178745
|
-
label: s.label,
|
|
178746
|
-
module: s.module,
|
|
178747
|
-
args: this.typedArgs(args),
|
|
178748
|
-
enabled: s.enabled
|
|
178749
|
-
});
|
|
178750
|
-
expected.set(s.id, args);
|
|
178751
|
-
return row;
|
|
178752
|
-
} catch {
|
|
178753
|
-
unconverted.add(s.id);
|
|
178754
|
-
return s;
|
|
178755
|
-
}
|
|
178756
|
-
});
|
|
178757
|
-
const failedRows = unconverted.size;
|
|
178758
|
-
if (expected.size === 0) return report({
|
|
178759
|
-
failed: failedRows,
|
|
178760
|
-
remainingRaw: legacy.length
|
|
178761
|
-
});
|
|
178762
|
-
try {
|
|
178763
|
-
await this.persistSources(migrated);
|
|
178764
|
-
} catch {
|
|
178765
|
-
return report({ failed: legacy.length });
|
|
178766
|
-
}
|
|
178767
|
-
const back = new Map(this.configuredSources().map((s) => [s.id, s]));
|
|
178768
|
-
if (![...expected].every(([id, args]) => {
|
|
178769
|
-
const row = back.get(id);
|
|
178770
|
-
return row !== void 0 && row.argsJson === void 0 && JSON.stringify(sortedEntries(row.args)) === JSON.stringify(sortedEntries(args));
|
|
178771
|
-
}) || this.unresolvedSecrets.length > 0) {
|
|
178772
|
-
await this.rollbackMigration(sources, expected);
|
|
178773
|
-
return report({ failed: legacy.length });
|
|
178774
|
-
}
|
|
178775
|
-
return report({
|
|
178776
|
-
migrated: expected.size,
|
|
178777
|
-
failed: failedRows,
|
|
178778
|
-
remainingRaw: failedRows,
|
|
178779
|
-
unresolvedMarkers: 0,
|
|
178780
|
-
redundantCopies: legacy.length,
|
|
178781
|
-
verified: failedRows === 0
|
|
178782
|
-
});
|
|
178783
|
-
}
|
|
178784
|
-
/**
|
|
178785
|
-
* Put the legacy rows back. A converted row's `args` is written as `null`, which
|
|
178786
|
-
* the save path reads as "this object is gone": it deletes the keychain refs the
|
|
178787
|
-
* migration created (a plain `{}` would keep them). Never throws - the report
|
|
178788
|
-
* says failed either way, and the legacy JSON stays authoritative.
|
|
178789
|
-
*/
|
|
178790
|
-
async rollbackMigration(sources, converted) {
|
|
178791
|
-
try {
|
|
178792
|
-
await this.updateGlobalSettingsAsOperator({ sources: sources.map((s) => converted.has(s.id) ? {
|
|
178793
|
-
...s,
|
|
178794
|
-
args: null
|
|
178795
|
-
} : s) });
|
|
178796
|
-
} catch {
|
|
178797
|
-
this.ctx.logger.error("waste-collections: the migration rollback failed - keychain refs may be orphaned (System -> Secrets)");
|
|
178798
|
-
}
|
|
178799
|
-
}
|
|
178800
|
-
async deleteSource(input) {
|
|
178801
|
-
const sources = this.configuredSources();
|
|
178802
|
-
if (!sources.some((s) => s.id === input.id)) throw new Error(`source '${input.id}' not found`);
|
|
178803
|
-
await this.persistSources(sources.filter((s) => s.id !== input.id));
|
|
178804
|
-
return { ok: true };
|
|
178805
|
-
}
|
|
178806
178276
|
async fetchNow(input) {
|
|
178807
178277
|
const targets = this.configuredSources().filter((s) => {
|
|
178808
178278
|
if (!s.enabled) return false;
|
|
@@ -178903,33 +178373,11 @@ var WasteCollectionsAddon = class extends BaseAddon {
|
|
|
178903
178373
|
this.ctx.logger.warn("waste-collections: cache persist failed", { meta: { error: err instanceof Error ? err.message : String(err) } });
|
|
178904
178374
|
}
|
|
178905
178375
|
}
|
|
178906
|
-
globalSettingsSchema() {
|
|
178907
|
-
return this.schema({ sections: [{
|
|
178908
|
-
id: "refresh",
|
|
178909
|
-
title: "Refresh",
|
|
178910
|
-
description: "Sources are managed in the table above (Add / Edit): pick a country and a service, then fill only that service’s fields. Each source becomes one calendar under System → Calendars.",
|
|
178911
|
-
columns: 2,
|
|
178912
|
-
fields: [{
|
|
178913
|
-
type: "addon-action-button",
|
|
178914
|
-
key: "_fetchNow",
|
|
178915
|
-
label: "Refresh",
|
|
178916
|
-
buttonLabel: "Fetch now",
|
|
178917
|
-
description: "Sources refresh once at boot and then every 24 hours; this refreshes every enabled source immediately.",
|
|
178918
|
-
addonId: "waste-collections",
|
|
178919
|
-
action: "fetchNow",
|
|
178920
|
-
variant: "primary",
|
|
178921
|
-
paramsStatic: {}
|
|
178922
|
-
}]
|
|
178923
|
-
}] });
|
|
178924
|
-
}
|
|
178925
178376
|
};
|
|
178926
178377
|
/** Re-parse helper for tests. */
|
|
178927
178378
|
function parseWasteConfig(raw) {
|
|
178928
178379
|
return WasteConfigSchema.parse(raw);
|
|
178929
178380
|
}
|
|
178930
|
-
function sortedEntries(args) {
|
|
178931
|
-
return Object.entries(args).toSorted(([a], [b]) => a < b ? -1 : 1);
|
|
178932
|
-
}
|
|
178933
178381
|
//#endregion
|
|
178934
178382
|
exports.DEFAULT_WASTE_CONFIG = DEFAULT_WASTE_CONFIG;
|
|
178935
178383
|
exports.WasteCollectionsAddon = WasteCollectionsAddon;
|