From 4e734a056f0926dc3702fa2c99f3d76dccfb5f12 Mon Sep 17 00:00:00 2001 From: afif Date: Tue, 22 Sep 2026 16:31:59 +0800 Subject: [PATCH 01/17] Establish installed private-control source baseline --- .gitignore | 2 + node_modules | 1 + package.json | 3 +- pnpm-lock.yaml | 10 + ...eRuntime-CfddDOOs.js => Layer-nAKmzBoW.js} | 549 +- .../chunks/effect-services-BVazUC0E.js | 171 + .../chunks/effect-services-BrlJlqgg.js | 85 - skills/chrome-control/chunks/rpc-BTiTvO3J.js | 5403 +++++++++++++++ skills/chrome-control/native-host/client.js | 219 +- skills/chrome-control/native-host/host.js | 5796 +---------------- .../chrome-control/native-host/transport.js | 419 ++ .../scripts/check-extension-installed.js | 24 +- .../scripts/check-native-host-manifest.js | 18 +- .../scripts/chrome-is-running.js | 8 +- .../scripts/install-native-host.js | 18 +- .../scripts/installed-browsers.js | 12 +- .../scripts/open-chrome-window.js | 8 +- src/extension/background.ts | 376 +- src/extension/manifest.json | 3 +- src/extension/page-control.ts | 181 + src/extension/private-input.ts | 56 + src/native-host/client.ts | 168 +- src/native-host/host.ts | 377 +- src/native-host/transport.ts | 205 + src/shared/page-protocol.ts | 34 + tests/acceptance/distribution.test.ts | 31 +- tests/security/background.test.ts | 247 + tests/security/client.test.ts | 74 + tests/security/host.test.ts | 214 + tests/security/page-diagnostics.test.ts | 41 + tests/security/private-input.browser.test.ts | 159 + tests/security/scoped-observation.test.ts | 206 + tests/security/tcp.test.ts | 62 + tests/security/transport.test.ts | 66 + tests/support/background.ts | 45 + tests/support/scoped-dom.ts | 83 + tests/support/temp.ts | 9 + vite.node.config.ts | 1 + 38 files changed, 8991 insertions(+), 6393 deletions(-) create mode 120000 node_modules rename skills/chrome-control/chunks/{NodeRuntime-CfddDOOs.js => Layer-nAKmzBoW.js} (96%) create mode 100644 skills/chrome-control/chunks/effect-services-BVazUC0E.js delete mode 100644 skills/chrome-control/chunks/effect-services-BrlJlqgg.js create mode 100644 skills/chrome-control/chunks/rpc-BTiTvO3J.js create mode 100644 skills/chrome-control/native-host/transport.js create mode 100644 src/extension/page-control.ts create mode 100644 src/extension/private-input.ts create mode 100644 src/native-host/transport.ts create mode 100644 src/shared/page-protocol.ts create mode 100644 tests/security/background.test.ts create mode 100644 tests/security/client.test.ts create mode 100644 tests/security/host.test.ts create mode 100644 tests/security/page-diagnostics.test.ts create mode 100644 tests/security/private-input.browser.test.ts create mode 100644 tests/security/scoped-observation.test.ts create mode 100644 tests/security/tcp.test.ts create mode 100644 tests/security/transport.test.ts create mode 100644 tests/support/background.ts create mode 100644 tests/support/scoped-dom.ts create mode 100644 tests/support/temp.ts diff --git a/.gitignore b/.gitignore index 198fca7..07b540e 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,5 @@ node_modules/ coverage/ dist/ scripts/extension-id.json +experiments/artifacts/ +experiments/chrome-jev/ready.json diff --git a/node_modules b/node_modules new file mode 120000 index 0000000..4062027 --- /dev/null +++ b/node_modules @@ -0,0 +1 @@ +/Users/afif.alauddin/.config/opencode/local/preview-private-input-20260922/op-chrome/node_modules \ No newline at end of file diff --git a/package.json b/package.json index f752bf1..b307b51 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "opzero-chrome", - "version": "0.1.3", + "version": "0.2.0", "private": true, "description": "Opzero Chrome extension with native messaging and CDP bridge.", "packageManager": "pnpm@10.33.2", @@ -26,6 +26,7 @@ "devDependencies": { "@types/chrome": "^0.1.42", "@types/node": "^25.7.0", + "playwright-core": "^1.63.0", "typescript": "^6.0.3", "vite": "^8.0.12", "vitest": "^4.1.6" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d2588f6..c27c1d4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -21,6 +21,9 @@ importers: '@types/node': specifier: ^25.7.0 version: 25.7.0 + playwright-core: + specifier: ^1.63.0 + version: 1.63.0 typescript: specifier: ^6.0.3 version: 6.0.3 @@ -572,6 +575,11 @@ packages: resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} engines: {node: '>=12'} + playwright-core@1.63.0: + resolution: {integrity: sha512-rYCsBF/M5HjUch52bbtVONEFjv6Xu8sm8h72dNlR5bzIE1fvC/bxgspzkjSfU+MweEMmPM8KJebG6nnyxo5mCg==} + engines: {node: '>=20'} + hasBin: true + postcss@8.5.14: resolution: {integrity: sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg==} engines: {node: ^10 || ^12 || >=14} @@ -1167,6 +1175,8 @@ snapshots: picomatch@4.0.4: {} + playwright-core@1.63.0: {} + postcss@8.5.14: dependencies: nanoid: 3.3.12 diff --git a/skills/chrome-control/chunks/NodeRuntime-CfddDOOs.js b/skills/chrome-control/chunks/Layer-nAKmzBoW.js similarity index 96% rename from skills/chrome-control/chunks/NodeRuntime-CfddDOOs.js rename to skills/chrome-control/chunks/Layer-nAKmzBoW.js index 7021d9f..33f3069 100644 --- a/skills/chrome-control/chunks/NodeRuntime-CfddDOOs.js +++ b/skills/chrome-control/chunks/Layer-nAKmzBoW.js @@ -20,7 +20,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge enumerable: true }) : target, mod)); //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Function.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Function.js /** * Tests if a value is a `function`. * @@ -251,7 +251,7 @@ function pipe(a, ab, bc, cd, de, ef, fg, gh, hi) { } } //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Equivalence.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Equivalence.js /** * This module provides an implementation of the `Equivalence` type class, which defines a binary relation * that is reflexive, symmetric, and transitive. In other words, it defines a notion of equivalence between values of a certain type. @@ -299,7 +299,7 @@ var array$1 = (item) => make$24((self, that) => { return true; }); //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/GlobalValue.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/GlobalValue.js /** * The `GlobalValue` module ensures that a single instance of a value is created globally, * even when modules are imported multiple times (e.g., due to mixing CommonJS and ESM builds) @@ -348,7 +348,7 @@ var globalValue = (id, compute) => { return globalStore.get(id); }; //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Predicate.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Predicate.js /** * This module provides a collection of functions for working with predicates and refinements. * @@ -758,14 +758,14 @@ var isRecord = (input) => isRecordOrArray(input) && !Array.isArray(input); */ var isPromiseLike = (input) => hasProperty(input, "then") && isFunction(input.then); //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/errors.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/errors.js /** * @since 2.0.0 */ /** @internal */ var getBugErrorMessage = (message) => `BUG: ${message} - please report an issue at https://github.com/Effect-TS/effect/issues`; //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Utils.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Utils.js /** * @category constructors * @since 2.0.0 @@ -993,7 +993,7 @@ var internalCall = /* @__PURE__ */ standard.effect_internal_function(() => (/* @ } }.effect_internal_function; (function* () {}).constructor; //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Hash.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Hash.js /** * @since 2.0.0 */ @@ -1122,7 +1122,7 @@ var cached = function() { return hash; }; //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Equal.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Equal.js /** * @since 2.0.0 * @category symbols @@ -1173,7 +1173,7 @@ var isEqual = (u) => hasProperty(u, symbol); */ var equivalence = () => equals$1; //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Inspectable.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Inspectable.js /** * @since 2.0.0 * @category symbols @@ -1319,7 +1319,7 @@ var redact = (u) => { return u; }; //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Pipeable.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Pipeable.js /** * @since 2.0.0 */ @@ -1346,7 +1346,7 @@ var pipeArguments = (self, args) => { } }; //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/opCodes/effect.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/opCodes/effect.js /** @internal */ var OP_ASYNC = "Async"; /** @internal */ @@ -1376,11 +1376,11 @@ var OP_YIELD = "Yield"; /** @internal */ var OP_REVERT_FLAGS = "RevertFlags"; //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/version.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/version.js var moduleVersion = "3.21.2"; var getCurrentVersion = () => moduleVersion; //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/effectable.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/effectable.js /** @internal */ var EffectTypeId$1 = /* @__PURE__ */ Symbol.for("effect/Effect"); /** @internal */ @@ -1476,7 +1476,7 @@ var Base$1 = /* @__PURE__ */ function() { return Base; }(); //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/option.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/option.js /** * @since 2.0.0 */ @@ -1540,7 +1540,7 @@ var some$1 = (value) => { return a; }; //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/either.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/either.js /** * @since 2.0.0 */ @@ -1613,7 +1613,7 @@ var right$1 = (right) => { /** @internal */ var fromOption$1 = /* @__PURE__ */ dual(2, (self, onNone) => isNone$1(self) ? left$1(onNone()) : right$1(self.value)); //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Either.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Either.js /** * Constructs a new `Either` holding a `Right` value. This usually represents a successful value due to the right bias * of this structure. @@ -1801,14 +1801,14 @@ var getOrThrowWith$1 = /* @__PURE__ */ dual(2, (self, onLeft) => { */ var getOrThrow = /* @__PURE__ */ getOrThrowWith$1(() => /* @__PURE__ */ new Error("getOrThrow called on a Left")); //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/array.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/array.js /** * @since 2.0.0 */ /** @internal */ var isNonEmptyArray$1 = (self) => self.length > 0; //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Order.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Order.js /** * This module provides an implementation of the `Order` type class which is used to define a total ordering on some type `A`. * An order is defined by a relation `<=`, which obeys the following laws: @@ -1849,7 +1849,7 @@ var mapInput = /* @__PURE__ */ dual(2, (self, f) => make$23((b1, b2) => self(f(b */ var greaterThan$1 = (O) => dual(2, (self, that) => O(self, that) === 1); //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Option.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Option.js /** * Represents the absence of a value by creating an empty `Option`. * @@ -2564,7 +2564,7 @@ var contains = /* @__PURE__ */ containsWith(/* @__PURE__ */ equivalence()); */ var exists = /* @__PURE__ */ dual(2, (self, refinement) => isNone(self) ? false : refinement(self.value)); //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Tuple.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Tuple.js /** * Constructs a new tuple from the provided values. * @@ -2581,7 +2581,7 @@ var exists = /* @__PURE__ */ dual(2, (self, refinement) => isNone(self) ? false */ var make$22 = (...elements) => elements; //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Array.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Array.js /** * Creates a new `Array` of the specified length. * @@ -3270,7 +3270,7 @@ var dedupe = (self) => dedupeWith(self, equivalence()); */ var join$1 = /* @__PURE__ */ dual(2, (self, sep) => fromIterable$6(self).join(sep)); //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Number.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Number.js /** * @memberof Number * @since 2.0.0 @@ -3295,7 +3295,7 @@ var parse = (s) => { return Number.isNaN(n) ? none$5 : some$1(n); }; //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/RegExp.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/RegExp.js /** * Escapes special characters in a regular expression pattern. * @@ -3311,7 +3311,7 @@ var parse = (s) => { */ var escape = (string) => string.replace(/[/\\^$*+?.()|[\]{}]/g, "\\$&"); //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Boolean.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Boolean.js /** * Negates the given boolean: `!self` * @@ -3329,7 +3329,7 @@ var escape = (string) => string.replace(/[/\\^$*+?.()|[\]{}]/g, "\\$&"); */ var not = (self) => !self; //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/context.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/context.js /** @internal */ var TagTypeId = /* @__PURE__ */ Symbol.for("effect/Context/Tag"); /** @internal */ @@ -3524,7 +3524,7 @@ var mergeAll$2 = (...ctxs) => { return makeContext(map); }; //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Context.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Context.js /** * Creates a new `Tag` instance with an optional key parameter. * @@ -3823,7 +3823,7 @@ var Tag = Tag$1; */ var Reference = Reference$1; //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Chunk.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Chunk.js /** * @since 2.0.0 */ @@ -4235,7 +4235,7 @@ var headNonEmpty = unsafeHead; */ var tailNonEmpty = (self) => drop(self, 1); //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Duration.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Duration.js /** * @since 2.0.0 */ @@ -4591,7 +4591,7 @@ var MAX_INDEX_NODE = BUCKET_SIZE / 2; /** @internal */ var MIN_ARRAY_NODE = BUCKET_SIZE / 4; //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/hashMap/bitwise.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/hashMap/bitwise.js /** * Hamming weight. * @@ -4620,13 +4620,13 @@ function fromBitmap(bitmap, bit) { return popcount(bitmap & bit - 1); } //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/stack.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/stack.js var make$17 = (value, previous) => ({ value, previous }); //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/hashMap/array.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/hashMap/array.js /** @internal */ function arrayUpdate(mutate, at, v, arr) { let out = arr; @@ -4671,7 +4671,7 @@ function arraySpliceIn(mutate, at, v, arr) { return out; } //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/hashMap/node.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/hashMap/node.js /** @internal */ var EmptyNode = class EmptyNode { _tag = "EmptyNode"; @@ -4903,7 +4903,7 @@ function mergeLeaves(edit, shift, h1, n1, h2, n2) { } } //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/hashMap.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/hashMap.js var HashMapSymbolKey = "effect/HashMap"; /** @internal */ var HashMapTypeId = /* @__PURE__ */ Symbol.for(HashMapSymbolKey); @@ -5116,7 +5116,7 @@ var reduce$5 = /* @__PURE__ */ dual(3, (self, zero, f) => { return zero; }); //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/hashSet.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/hashSet.js var HashSetSymbolKey = "effect/HashSet"; /** @internal */ var HashSetTypeId = /* @__PURE__ */ Symbol.for(HashSetSymbolKey); @@ -5206,7 +5206,7 @@ var forEach$2 = /* @__PURE__ */ dual(2, (self, f) => forEach$3(self._keyMap, (_, /** @internal */ var reduce$4 = /* @__PURE__ */ dual(3, (self, zero, f) => reduce$5(self._keyMap, zero, (z, _, a) => f(z, a))); //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/HashSet.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/HashSet.js /** * # HashSet * @@ -5872,7 +5872,7 @@ var union = union$1; */ var reduce$3 = reduce$4; //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/MutableRef.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/MutableRef.js var TypeId$6 = /* @__PURE__ */ Symbol.for("effect/MutableRef"); var MutableRefProto = { [TypeId$6]: TypeId$6, @@ -5915,7 +5915,7 @@ var set$3 = /* @__PURE__ */ dual(2, (self, value) => { return self; }); //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/fiberId.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/fiberId.js /** @internal */ var FiberIdSymbolKey = "effect/FiberId"; /** @internal */ @@ -6047,7 +6047,7 @@ var unsafeMake$5 = () => { return new Runtime(id, Date.now()); }; //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/FiberId.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/FiberId.js /** * @since 2.0.0 * @category constructors @@ -6087,7 +6087,7 @@ var threadName = threadName$1; */ var unsafeMake$4 = unsafeMake$5; //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/HashMap.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/HashMap.js /** * @since 2.0.0 */ @@ -6167,7 +6167,7 @@ var map$2 = map$3; */ var reduce$2 = reduce$5; //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/List.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/List.js /** * A data type for immutable linked lists representing ordered collections of elements of type `A`. * @@ -6452,7 +6452,7 @@ var Structural = /* @__PURE__ */ function() { return Structural; }(); //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/differ/contextPatch.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/differ/contextPatch.js /** @internal */ var ContextPatchTypeId = /* @__PURE__ */ Symbol.for("effect/DifferContextPatch"); function variance$3(a) { @@ -6556,7 +6556,7 @@ var patch$7 = /* @__PURE__ */ dual(2, (self, context) => { return makeContext(map); }); //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/differ/hashSetPatch.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/differ/hashSetPatch.js /** @internal */ var HashSetPatchTypeId = /* @__PURE__ */ Symbol.for("effect/DifferHashSetPatch"); function variance$2(a) { @@ -6633,7 +6633,7 @@ var patch$6 = /* @__PURE__ */ dual(2, (self, oldValue) => { return set; }); //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/differ/readonlyArrayPatch.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/differ/readonlyArrayPatch.js /** @internal */ var ReadonlyArrayPatchTypeId = /* @__PURE__ */ Symbol.for("effect/DifferReadonlyArrayPatch"); function variance$1(a) { @@ -6788,7 +6788,7 @@ var updateWith = (f) => make$13({ patch: (patch, oldValue) => f(oldValue, patch(oldValue)) }); //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/runtimeFlagsPatch.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/runtimeFlagsPatch.js /** @internal */ var BIT_MASK = 255; /** @internal */ @@ -6840,7 +6840,7 @@ var differ$1 = /* @__PURE__ */ make$13({ patch: (_patch, oldValue) => patch$4(oldValue, _patch) }); //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/RuntimeFlagsPatch.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/RuntimeFlagsPatch.js /** * Creates a `RuntimeFlagsPatch` describing enabling the provided `RuntimeFlag`. * @@ -6864,7 +6864,7 @@ var disable = disable$1; */ var exclude = exclude$1; //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/blockedRequests.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/blockedRequests.js /** * Combines this collection of blocked requests with the specified collection * of blocked requests, in parallel. @@ -7038,7 +7038,7 @@ var OP_PARALLEL$1 = "Parallel"; /** @internal */ var OP_SEQUENTIAL$1 = "Sequential"; //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/cause.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/cause.js /** @internal */ var CauseSymbolKey = "effect/Cause"; /** @internal */ @@ -7533,7 +7533,7 @@ var prettyErrors = (cause) => reduceWithContext(cause, void 0, { sequentialCase: (_, l, r) => [...l, ...r] }); //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/opCodes/deferred.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/opCodes/deferred.js /** @internal */ var OP_STATE_PENDING = "Pending"; /** @internal */ @@ -7562,7 +7562,7 @@ var done$2 = (effect) => { }; }; //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/singleShotGen.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/singleShotGen.js /** @internal */ var SingleShotGen = class SingleShotGen { self; @@ -7593,7 +7593,7 @@ var SingleShotGen = class SingleShotGen { } }; //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/core.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/core.js /** * @internal */ @@ -8036,10 +8036,6 @@ var zip = /* @__PURE__ */ dual(2, (self, that) => flatMap$2(self, (a) => map$1(t var zipLeft = /* @__PURE__ */ dual(2, (self, that) => flatMap$2(self, (a) => as(that, a))); var zipRight = /* @__PURE__ */ dual(2, (self, that) => flatMap$2(self, () => that)); var zipWith = /* @__PURE__ */ dual(3, (self, that, f) => flatMap$2(self, (a) => map$1(that, (b) => f(a, b)))); -var never$1 = /* @__PURE__ */ asyncInterrupt(() => { - const interval = setInterval(() => {}, 2 ** 31 - 1); - return sync$1(() => clearInterval(interval)); -}); var interruptFiber = (self) => flatMap$2(fiberId, (fiberId) => pipe(self, interruptAsFiber(fiberId))); var interruptAsFiber = /* @__PURE__ */ dual(2, (self, fiberId) => flatMap$2(self.interruptAsFork(fiberId), () => self.await)); /** @internal */ @@ -8436,7 +8432,7 @@ var currentSpanFromFiber = (fiber) => { return span !== void 0 && span._tag === "Span" ? some(span) : none$4(); }; //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Exit.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Exit.js /** * Returns `true` if the specified `Exit` is a `Failure`, `false` otherwise. * @@ -8452,7 +8448,7 @@ var isFailure = exitIsFailure; */ var isSuccess = exitIsSuccess; //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/MutableHashMap.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/MutableHashMap.js var TypeId$4 = /* @__PURE__ */ Symbol.for("effect/MutableHashMap"); var MutableHashMapProto = { [TypeId$4]: TypeId$4, @@ -8762,7 +8758,7 @@ var prefixed = /* @__PURE__ */ dual(2, (self, prefix) => { } }); //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/configProvider/pathPatch.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/configProvider/pathPatch.js /** @internal */ var empty$4 = { _tag: "Empty" }; /** @internal */ @@ -8797,7 +8793,7 @@ var patch$3 = /* @__PURE__ */ dual(2, (path, patch) => { return right(output); }); //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/opCodes/config.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/opCodes/config.js /** @internal */ var OP_CONSTANT = "Constant"; /** @internal */ @@ -8821,7 +8817,7 @@ var OP_HASHMAP = "HashMap"; /** @internal */ var OP_ZIP_WITH = "ZipWith"; //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/configProvider.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/configProvider.js var concat = (l, r) => [...l, ...r]; /** @internal */ var ConfigProviderTypeId = /* @__PURE__ */ Symbol.for("effect/ConfigProvider"); @@ -8979,7 +8975,7 @@ var parseInteger = (str) => { return Number.isNaN(parsedIndex) ? none$4() : some(parsedIndex); }; //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/defaultServices/console.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/defaultServices/console.js /** @internal */ var TypeId$3 = /* @__PURE__ */ Symbol.for("effect/Console"); /** @internal */ @@ -9116,7 +9112,7 @@ var swap = (buffer, index1, index2) => { }; var make$8 = (seed) => new RandomImpl(hash(seed)); //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/tracer.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/tracer.js /** * @since 2.0.0 */ @@ -9200,7 +9196,7 @@ var nativeTracer = /* @__PURE__ */ make$7({ /** @internal */ var DisablePropagation = /* @__PURE__ */ Reference()("effect/Tracer/DisablePropagation", { defaultValue: constFalse }); //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/defaultServices.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/defaultServices.js /** @internal */ var liveServices = /* @__PURE__ */ pipe(/* @__PURE__ */ empty$17(), /* @__PURE__ */ add$2(clockTag, /* @__PURE__ */ make$10()), /* @__PURE__ */ add$2(consoleTag, defaultConsole), /* @__PURE__ */ add$2(randomTag, /* @__PURE__ */ make$8(/* @__PURE__ */ Math.random())), /* @__PURE__ */ add$2(configProviderTag, /* @__PURE__ */ fromEnv()), /* @__PURE__ */ add$2(tracerTag, nativeTracer)); /** @@ -9211,7 +9207,7 @@ var liveServices = /* @__PURE__ */ pipe(/* @__PURE__ */ empty$17(), /* @__PURE__ */ var currentServices = /* @__PURE__ */ globalValue(/* @__PURE__ */ Symbol.for("effect/DefaultServices/currentServices"), () => fiberRefUnsafeMakeContext(liveServices)); //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/fiberRefs.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/fiberRefs.js /** @internal */ function unsafeMake$3(fiberRefLocals) { return new FiberRefsImpl(fiberRefLocals); @@ -9346,7 +9342,7 @@ var updateManyAs$1 = /* @__PURE__ */ dual(2, (self, { entries, forkAs }) => { return new FiberRefsImpl(locals); }); //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/FiberRefs.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/FiberRefs.js /** * Gets the value of the specified `FiberRef` in this collection of `FiberRef` * values if it exists or the `initial` value of the `FiberRef` otherwise. @@ -9377,7 +9373,7 @@ var updateManyAs = updateManyAs$1; */ var empty$2 = empty$3; //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/LogLevel.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/LogLevel.js /** * @since 2.0.0 * @category constructors @@ -9440,7 +9436,7 @@ var fromLiteral = (literal) => { } }; //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/logSpan.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/logSpan.js /** * Sanitize a given string by replacing spaces, equal signs, and double quotes with underscores. * @@ -9452,7 +9448,7 @@ var render = (now) => (self) => { return `${formatLabel(self.label)}=${now - self.startTime}ms`; }; //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Effectable.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Effectable.js /** * @since 2.0.0 * @category prototypes @@ -9465,14 +9461,14 @@ var Base = Base$1; */ var Class = class extends Base {}; //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Readable.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Readable.js /** * @since 2.0.0 * @category type ids */ var TypeId$2 = /* @__PURE__ */ Symbol.for("effect/Readable"); //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/ref.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/ref.js /** @internal */ var RefTypeId = /* @__PURE__ */ Symbol.for("effect/Ref"); /** @internal */ @@ -9514,7 +9510,7 @@ var modify = /* @__PURE__ */ dual(2, (self, f) => self.modify(f)); /** @internal */ var update$1 = /* @__PURE__ */ dual(2, (self, f) => self.modify((a) => [void 0, f(a)])); //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/fiberRefs/patch.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/fiberRefs/patch.js /** @internal */ var OP_EMPTY$1 = "Empty"; /** @internal */ @@ -9599,7 +9595,7 @@ var patch$2 = /* @__PURE__ */ dual(3, (self, fiberId, oldValue) => { return fiberRefs; }); //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/metric/label.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/metric/label.js /** @internal */ var MetricLabelSymbolKey = "effect/MetricLabel"; /** @internal */ @@ -9632,7 +9628,7 @@ var make$5 = (key, value) => { /** @internal */ var isMetricLabel = (u) => hasProperty(u, MetricLabelTypeId); //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/core-effect.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/core-effect.js var asSome = (self) => map$1(self, some); var try_$1 = (arg) => { let evaluate; @@ -9681,37 +9677,13 @@ var tapErrorCause$1 = /* @__PURE__ */ dual(2, (self, f) => matchCauseEffect$1(se onFailure: (cause) => zipRight(f(cause), failCause$1(cause)), onSuccess: succeed$3 })); -var tryPromise$1 = (arg) => { - let evaluate; - let catcher = void 0; - if (typeof arg === "function") evaluate = arg; - else { - evaluate = arg.try; - catcher = arg.catch; - } - const fail = (e) => catcher ? failSync(() => catcher(e)) : fail$2(new UnknownException(e, "An unknown error occurred in Effect.tryPromise")); - if (evaluate.length >= 1) return async_((resolve, signal) => { - try { - evaluate(signal).then((a) => resolve(succeed$3(a)), (e) => resolve(fail(e))); - } catch (e) { - resolve(fail(e)); - } - }); - return async_((resolve) => { - try { - evaluate().then((a) => resolve(succeed$3(a)), (e) => resolve(fail(e))); - } catch (e) { - resolve(fail(e)); - } - }); -}; var updateFiberRefs = (f) => withFiberRuntime((state) => { state.setFiberRefs(f(state.id(), state.getFiberRefs())); return void_$1; }); var filterDisablePropagation = /* @__PURE__ */ flatMap$4((span) => get$6(span.context, DisablePropagation) ? span._tag === "Span" ? filterDisablePropagation(span.parent) : none$4() : some(span)); //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/executionStrategy.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/executionStrategy.js /** @internal */ var OP_SEQUENTIAL = "Sequential"; /** @internal */ @@ -9732,7 +9704,7 @@ var isSequential = (self) => self._tag === OP_SEQUENTIAL; /** @internal */ var isParallel = (self) => self._tag === OP_PARALLEL; //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/ExecutionStrategy.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/ExecutionStrategy.js /** * Execute effects sequentially. * @@ -9755,7 +9727,7 @@ var parallel = parallel$1; */ var parallelN = parallelN$1; //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/FiberRefsPatch.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/FiberRefsPatch.js /** * Constructs a patch that describes the changes between the specified * collections of `FiberRef` @@ -9773,7 +9745,7 @@ var diff$1 = diff$2; */ var patch$1 = patch$2; //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/fiberStatus.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/fiberStatus.js var FiberStatusSymbolKey = "effect/FiberStatus"; /** @internal */ var FiberStatusTypeId = /* @__PURE__ */ Symbol.for(FiberStatusSymbolKey); @@ -9838,7 +9810,7 @@ var isFiberStatus = (u) => hasProperty(u, FiberStatusTypeId); /** @internal */ var isDone$1 = (self) => self._tag === OP_DONE; //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/FiberStatus.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/FiberStatus.js /** * @since 2.0.0 * @category constructors @@ -9862,7 +9834,7 @@ var suspended = suspended$1; */ var isDone = isDone$1; //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Micro.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Micro.js /** * @since 3.4.0 * @experimental @@ -10539,7 +10511,7 @@ var TaggedError = (tag) => { TaggedError("NoSuchElementException"); TaggedError("TimeoutException"); //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Scheduler.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Scheduler.js /** * @since 3.20.0 * @category models @@ -10681,11 +10653,11 @@ var SyncScheduler = class { /** @internal */ var currentScheduler = /* @__PURE__ */ globalValue(/* @__PURE__ */ Symbol.for("effect/FiberRef/currentScheduler"), () => fiberRefUnsafeMake(defaultScheduler)); //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/completedRequestMap.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/completedRequestMap.js /** @internal */ var currentRequestMap = /* @__PURE__ */ globalValue(/* @__PURE__ */ Symbol.for("effect/FiberRef/currentRequestMap"), () => fiberRefUnsafeMake(/* @__PURE__ */ new Map())); //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/concurrency.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/concurrency.js /** @internal */ var match = (concurrency, sequential, unbounded, bounded) => { switch (concurrency) { @@ -10696,7 +10668,7 @@ var match = (concurrency, sequential, unbounded, bounded) => { } }; //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/fiberMessage.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/fiberMessage.js /** @internal */ var OP_INTERRUPT_SIGNAL = "InterruptSignal"; /** @internal */ @@ -10960,9 +10932,9 @@ var prettyLoggerBrowser = (options) => { }); }; /** @internal */ -var prettyLoggerDefault$1 = /* @__PURE__ */ globalValue("effect/Logger/prettyLoggerDefault", () => prettyLogger()); +var prettyLoggerDefault = /* @__PURE__ */ globalValue("effect/Logger/prettyLoggerDefault", () => prettyLogger()); //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/metric/boundaries.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/metric/boundaries.js /** @internal */ var MetricBoundariesSymbolKey = "effect/MetricBoundaries"; /** @internal */ @@ -11822,7 +11794,7 @@ var fromEffect = (effect) => { /** @internal */ var none = /* @__PURE__ */ globalValue("effect/Supervisor/none", () => fromEffect(void_$1)); //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Differ.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Differ.js /** * Constructs a new `Differ`. * @@ -11831,7 +11803,7 @@ var none = /* @__PURE__ */ globalValue("effect/Supervisor/none", () => fromEffec */ var make$1 = make$13; //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/supervisor/patch.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/supervisor/patch.js /** @internal */ var OP_EMPTY = "Empty"; /** @internal */ @@ -11925,7 +11897,7 @@ var differ = /* @__PURE__ */ make$1({ diff }); //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/fiberRuntime.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/fiberRuntime.js /** @internal */ var fiberStarted = /* @__PURE__ */ counter("effect_fiber_started", { incremental: true }); /** @internal */ @@ -12469,7 +12441,7 @@ var FiberRuntime = class extends Class { this._observers = []; } getLoggers() { - return this.getFiberRef(currentLoggers$1); + return this.getFiberRef(currentLoggers); } log(message, cause, overrideLogLevel) { const logLevel = isSome(overrideLogLevel) ? overrideLogLevel.value : this.getFiberRef(currentLogLevel); @@ -12871,7 +12843,7 @@ var loggerWithConsoleLog = (self) => makeLogger((opts) => { get$6(getOrDefault(opts.context, currentServices), consoleTag).unsafe.log(self.log(opts)); }); /** @internal */ -var defaultLogger$1 = /* @__PURE__ */ globalValue(/* @__PURE__ */ Symbol.for("effect/Logger/defaultLogger"), () => loggerWithConsoleLog(stringLogger)); +var defaultLogger = /* @__PURE__ */ globalValue(/* @__PURE__ */ Symbol.for("effect/Logger/defaultLogger"), () => loggerWithConsoleLog(stringLogger)); /** @internal */ var tracerLogger = /* @__PURE__ */ globalValue(/* @__PURE__ */ Symbol.for("effect/Logger/tracerLogger"), () => makeLogger(({ annotations, cause, context, fiberId, logLevel, message }) => { const span = filterDisablePropagation(getOption(getOrDefault$1(context, currentContext), spanTag)); @@ -12885,7 +12857,7 @@ var tracerLogger = /* @__PURE__ */ globalValue(/* @__PURE__ */ Symbol.for("effec span.value.event(toStringUnknown(Array.isArray(message) && message.length === 1 ? message[0] : message), clockService.unsafeCurrentTimeNanos(), attributes); })); /** @internal */ -var currentLoggers$1 = /* @__PURE__ */ globalValue(/* @__PURE__ */ Symbol.for("effect/FiberRef/currentLoggers"), () => fiberRefUnsafeMakeHashSet(make$15(defaultLogger$1, tracerLogger))); +var currentLoggers = /* @__PURE__ */ globalValue(/* @__PURE__ */ Symbol.for("effect/FiberRef/currentLoggers"), () => fiberRefUnsafeMakeHashSet(make$15(defaultLogger, tracerLogger))); var allResolveInput = (input) => { if (Array.isArray(input) || isIterable(input)) return [input, none$4()]; const keys = Object.keys(input); @@ -13268,7 +13240,7 @@ var invokeWithInterrupt = (self, entries, onInterrupt) => fiberIdWith((id) => en }), (entry) => complete(entry.request, exitInterrupt$1(id))); }))); //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Cause.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Cause.js /** * Checks if a `Cause` is a `Fail` type. * @@ -13324,7 +13296,7 @@ var IllegalArgumentException = IllegalArgumentException$1; */ var pretty = pretty$1; //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Scope.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Scope.js /** * @since 2.0.0 */ @@ -13345,7 +13317,7 @@ var close = scopeClose; */ var fork = scopeFork; //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/effect/circular.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/effect/circular.js /** @internal */ var Semaphore = class { permits; @@ -13447,11 +13419,11 @@ var unsafeMakeSynchronized = (value) => { return new SynchronizedImpl(unsafeMake$2(value), unsafeMakeSemaphore(1).withPermits(1)); }; //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/managedRuntime/circular.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/managedRuntime/circular.js /** @internal */ var TypeId = /* @__PURE__ */ Symbol.for("effect/ManagedRuntime"); //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/opCodes/layer.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/opCodes/layer.js /** @internal */ var OP_FRESH = "Fresh"; /** @internal */ @@ -13459,7 +13431,7 @@ var OP_FROM_EFFECT = "FromEffect"; /** @internal */ var OP_MERGE_ALL = "MergeAll"; //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/runtime.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/runtime.js var makeDual = (f) => function() { if (arguments.length === 1) { const runtime = arguments[0]; @@ -13572,26 +13544,6 @@ var unsafeRunSyncExit = /* @__PURE__ */ makeDual((runtime, effect) => { return exitDie$1(capture(asyncFiberException(fiberRuntime), currentSpanFromFiber(fiberRuntime))); }); /** @internal */ -var unsafeRunPromise = /* @__PURE__ */ makeDual((runtime, effect, options) => unsafeRunPromiseExit(runtime, effect, options).then((result) => { - switch (result._tag) { - case OP_SUCCESS: return result.effect_instruction_i0; - case OP_FAILURE: throw fiberFailure(result.effect_instruction_i0); - } -})); -/** @internal */ -var unsafeRunPromiseExit = /* @__PURE__ */ makeDual((runtime, effect, options) => new Promise((resolve) => { - const op = fastPath(effect); - if (op) resolve(op); - const fiber = unsafeFork(runtime)(effect); - fiber.addObserver((exit) => { - resolve(exit); - }); - if (options?.signal !== void 0) if (options.signal.aborted) fiber.unsafeInterruptAsFork(fiber.id()); - else options.signal.addEventListener("abort", () => { - fiber.unsafeInterruptAsFork(fiber.id()); - }, { once: true }); -})); -/** @internal */ var RuntimeImpl = class { context; runtimeFlags; @@ -13616,11 +13568,9 @@ var defaultRuntime = /* @__PURE__ */ make({ /** @internal */ var unsafeForkEffect = /* @__PURE__ */ unsafeFork(defaultRuntime); /** @internal */ -var unsafeRunPromiseEffect = /* @__PURE__ */ unsafeRunPromise(defaultRuntime); -/** @internal */ var unsafeRunSyncEffect = /* @__PURE__ */ unsafeRunSync(defaultRuntime); //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/synchronizedRef.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/synchronizedRef.js /** @internal */ var modifyEffect = /* @__PURE__ */ dual(2, (self, f) => self.modifyEffect(f)); /** @internal */ @@ -13779,7 +13729,7 @@ var effect_provide = /* @__PURE__ */ dual(2, (self, source) => { else return provideSomeRuntime(self, source); }); //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Effect.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Effect.js /** * Checks if a given value is an `Effect` value. * @@ -13941,21 +13891,6 @@ var fail = fail$2; */ var gen = gen$1; /** -* An effect that that runs indefinitely and never produces any result. The -* moral equivalent of `while(true) {}`, only without the wasted CPU cycles. -* -* **When to Use** -* -* It could be useful for long-running background tasks or to simulate waiting -* behavior without actually consuming resources. This effect is ideal for cases -* where you want to keep the program alive or in a certain state without -* performing any active work. -* -* @since 2.0.0 -* @category Creating Effects -*/ -var never = never$1; -/** * Delays the creation of an `Effect` until it is actually needed. * * **Details** @@ -14151,71 +14086,6 @@ var _void = void_$1; var catchAll = catchAll$1; var try_ = try_$1; /** -* Creates an `Effect` that represents an asynchronous computation that might -* fail. -* -* **When to Use** -* -* In situations where you need to perform asynchronous operations that might -* fail, such as fetching data from an API, you can use the `tryPromise` -* constructor. This constructor is designed to handle operations that could -* throw exceptions by capturing those exceptions and transforming them into -* manageable errors. -* -* **Error Handling** -* -* There are two ways to handle errors with `tryPromise`: -* -* 1. If you don't provide a `catch` function, the error is caught and the -* effect fails with an `UnknownException`. -* 2. If you provide a `catch` function, the error is caught and the `catch` -* function maps it to an error of type `E`. -* -* **Interruptions** -* -* An optional `AbortSignal` can be provided to allow for interruption of the -* wrapped `Promise` API. -* -* **Example** (Fetching a TODO Item) -* -* ```ts -* import { Effect } from "effect" -* -* const getTodo = (id: number) => -* // Will catch any errors and propagate them as UnknownException -* Effect.tryPromise(() => -* fetch(`https://jsonplaceholder.typicode.com/todos/${id}`) -* ) -* -* // ┌─── Effect -* // ▼ -* const program = getTodo(1) -* ``` -* -* **Example** (Custom Error Handling) -* -* ```ts -* import { Effect } from "effect" -* -* const getTodo = (id: number) => -* Effect.tryPromise({ -* try: () => fetch(`https://jsonplaceholder.typicode.com/todos/${id}`), -* // remap the error -* catch: (unknown) => new Error(`something went wrong ${unknown}`) -* }) -* -* // ┌─── Effect -* // ▼ -* const program = getTodo(1) -* ``` -* -* @see {@link promise} if the effectful computation is asynchronous and does not throw errors. -* -* @since 2.0.0 -* @category Creating Effects -*/ -var tryPromise = tryPromise$1; -/** * Transforms the value inside an effect by applying a function to it. * * **Syntax** @@ -14642,51 +14512,6 @@ var logError = logError$1; */ var runFork = unsafeForkEffect; /** -* Executes an effect and returns the result as a `Promise`. -* -* **Details** -* -* This function runs an effect and converts its result into a `Promise`. If the -* effect succeeds, the `Promise` will resolve with the successful result. If -* the effect fails, the `Promise` will reject with an error, which includes the -* failure details of the effect. -* -* The optional `options` parameter allows you to pass an `AbortSignal` for -* cancellation, enabling more fine-grained control over asynchronous tasks. -* -* **When to Use** -* -* Use this function when you need to execute an effect and work with its result -* in a promise-based system, such as when integrating with third-party -* libraries that expect `Promise` results. -* -* **Example** (Running a Successful Effect as a Promise) -* -* ```ts -* import { Effect } from "effect" -* -* Effect.runPromise(Effect.succeed(1)).then(console.log) -* // Output: 1 -* ``` -* -* **Example** (Handling a Failing Effect as a Rejected Promise) -* -* ```ts -* import { Effect } from "effect" -* -* Effect.runPromise(Effect.fail("my error")).catch(console.error) -* // Output: -* // (FiberFailure) Error: my error -* ``` -* -* @see {@link runPromiseExit} for a version that returns an `Exit` type instead -* of rejecting. -* -* @since 2.0.0 -* @category Running Effects -*/ -var runPromise = unsafeRunPromiseEffect; -/** * Executes an effect synchronously, running it immediately and returning the * result. * @@ -14765,14 +14590,7 @@ var runPromise = unsafeRunPromiseEffect; */ var runSync = unsafeRunSyncEffect; //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/FiberRef.js -/** -* @since 2.0.0 -* @category fiberRefs -*/ -var currentLoggers = currentLoggers$1; -//#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Layer.js +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Layer.js /** * Constructs a layer from the specified value. * @@ -14781,85 +14599,6 @@ var currentLoggers = currentLoggers$1; */ var succeed = succeed$1; //#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Logger.js -/** -* @since 2.0.0 -* @category constructors -*/ -var defaultLogger = defaultLogger$1; -/** -* A default version of the pretty logger. -* -* @since 3.8.0 -* @category constructors -*/ -var prettyLoggerDefault = prettyLoggerDefault$1; -//#endregion -//#region node_modules/.pnpm/@effect+platform@0.96.1_effect@3.21.2/node_modules/@effect/platform/dist/esm/Runtime.js -/** -* @since 1.0.0 -*/ -/** -* @category teardown -* @since 1.0.0 -*/ -var defaultTeardown = (exit, onExit) => { - onExit(isFailure(exit) && !isInterruptedOnly(exit.cause) ? 1 : 0); -}; -var addPrettyLogger = (refs, fiberId) => { - const loggers = getOrDefault(refs, currentLoggers); - if (!has$1(loggers, defaultLogger)) return refs; - return updateAs(refs, { - fiberId, - fiberRef: currentLoggers, - value: loggers.pipe(remove(defaultLogger), add(prettyLoggerDefault)) - }); -}; -/** -* @category constructors -* @since 1.0.0 -*/ -var makeRunMain = (f) => dual((args) => isEffect(args[0]), (effect, options) => { - return f({ - fiber: options?.disableErrorReporting === true ? runFork(effect, { updateRefs: options?.disablePrettyLogger === true ? void 0 : addPrettyLogger }) : runFork(tapErrorCause(effect, (cause) => { - if (isInterruptedOnly(cause)) return _void; - return logError(cause); - }), { updateRefs: options?.disablePrettyLogger === true ? void 0 : addPrettyLogger }), - teardown: options?.teardown ?? defaultTeardown - }); -}); -//#endregion -//#region node_modules/.pnpm/@effect+platform-node@0.106.0_@effect+cluster@0.58.2_@effect+platform@0.96.1_effect@3.2_a80e93d5c9b0cafd3a2a57951ab69ada/node_modules/@effect/platform-node/dist/esm/NodeRuntime.js -/** -* @since 1.0.0 -*/ -/** -* @since 1.0.0 -* @category runtime -*/ -var runMain = /* @__PURE__ */ makeRunMain(({ fiber, teardown }) => { - const keepAlive = setInterval(constVoid, 2 ** 31 - 1); - let receivedSignal = false; - fiber.addObserver((exit) => { - if (!receivedSignal) { - process.removeListener("SIGINT", onSigint); - process.removeListener("SIGTERM", onSigint); - } - clearInterval(keepAlive); - teardown(exit, (code) => { - if (receivedSignal || code !== 0) process.exit(code); - }); - }); - function onSigint() { - receivedSignal = true; - process.removeListener("SIGINT", onSigint); - process.removeListener("SIGTERM", onSigint); - fiber.unsafeInterruptAsFork(fiber.id()); - } - process.on("SIGINT", onSigint); - process.on("SIGTERM", onSigint); -}); -//#endregion Object.defineProperty(exports, "Date", { enumerable: true, get: function() { @@ -14920,6 +14659,12 @@ Object.defineProperty(exports, "_void", { return _void; } }); +Object.defineProperty(exports, "add", { + enumerable: true, + get: function() { + return add; + } +}); Object.defineProperty(exports, "allocate", { enumerable: true, get: function() { @@ -14950,12 +14695,30 @@ Object.defineProperty(exports, "composite", { return composite; } }); +Object.defineProperty(exports, "constVoid", { + enumerable: true, + get: function() { + return constVoid; + } +}); Object.defineProperty(exports, "copy", { enumerable: true, get: function() { return copy$1; } }); +Object.defineProperty(exports, "currentLoggers", { + enumerable: true, + get: function() { + return currentLoggers; + } +}); +Object.defineProperty(exports, "defaultLogger", { + enumerable: true, + get: function() { + return defaultLogger; + } +}); Object.defineProperty(exports, "dual", { enumerable: true, get: function() { @@ -15076,6 +14839,12 @@ Object.defineProperty(exports, "getEquivalence$1", { return getEquivalence$3; } }); +Object.defineProperty(exports, "getOrDefault", { + enumerable: true, + get: function() { + return getOrDefault; + } +}); Object.defineProperty(exports, "getOrElse", { enumerable: true, get: function() { @@ -15112,6 +14881,12 @@ Object.defineProperty(exports, "globalValue", { return globalValue; } }); +Object.defineProperty(exports, "has", { + enumerable: true, + get: function() { + return has$1; + } +}); Object.defineProperty(exports, "hasProperty", { enumerable: true, get: function() { @@ -15184,6 +14959,12 @@ Object.defineProperty(exports, "isFailType", { return isFailType; } }); +Object.defineProperty(exports, "isFailure", { + enumerable: true, + get: function() { + return isFailure; + } +}); Object.defineProperty(exports, "isFiberId", { enumerable: true, get: function() { @@ -15196,6 +14977,12 @@ Object.defineProperty(exports, "isFinite", { return isFinite; } }); +Object.defineProperty(exports, "isInterruptedOnly", { + enumerable: true, + get: function() { + return isInterruptedOnly; + } +}); Object.defineProperty(exports, "isLeft", { enumerable: true, get: function() { @@ -15322,6 +15109,12 @@ Object.defineProperty(exports, "liftThrowable", { return liftThrowable; } }); +Object.defineProperty(exports, "logError", { + enumerable: true, + get: function() { + return logError; + } +}); Object.defineProperty(exports, "make", { enumerable: true, get: function() { @@ -15406,12 +15199,6 @@ Object.defineProperty(exports, "nanos", { return nanos; } }); -Object.defineProperty(exports, "never", { - enumerable: true, - get: function() { - return never; - } -}); Object.defineProperty(exports, "none", { enumerable: true, get: function() { @@ -15472,34 +15259,34 @@ Object.defineProperty(exports, "prettyErrorMessage", { return prettyErrorMessage; } }); -Object.defineProperty(exports, "provide", { +Object.defineProperty(exports, "prettyLoggerDefault", { enumerable: true, get: function() { - return provide; + return prettyLoggerDefault; } }); -Object.defineProperty(exports, "right", { +Object.defineProperty(exports, "provide", { enumerable: true, get: function() { - return right; + return provide; } }); -Object.defineProperty(exports, "runFork", { +Object.defineProperty(exports, "remove", { enumerable: true, get: function() { - return runFork; + return remove; } }); -Object.defineProperty(exports, "runMain", { +Object.defineProperty(exports, "right", { enumerable: true, get: function() { - return runMain; + return right; } }); -Object.defineProperty(exports, "runPromise", { +Object.defineProperty(exports, "runFork", { enumerable: true, get: function() { - return runPromise; + return runFork; } }); Object.defineProperty(exports, "runSync", { @@ -15562,22 +15349,22 @@ Object.defineProperty(exports, "sync", { return sync; } }); -Object.defineProperty(exports, "toMillis", { +Object.defineProperty(exports, "tapErrorCause", { enumerable: true, get: function() { - return toMillis; + return tapErrorCause; } }); -Object.defineProperty(exports, "toNanos", { +Object.defineProperty(exports, "toMillis", { enumerable: true, get: function() { - return toNanos; + return toMillis; } }); -Object.defineProperty(exports, "tryPromise", { +Object.defineProperty(exports, "toNanos", { enumerable: true, get: function() { - return tryPromise; + return toNanos; } }); Object.defineProperty(exports, "try_", { @@ -15592,3 +15379,9 @@ Object.defineProperty(exports, "try_$1", { return try_$2; } }); +Object.defineProperty(exports, "updateAs", { + enumerable: true, + get: function() { + return updateAs; + } +}); diff --git a/skills/chrome-control/chunks/effect-services-BVazUC0E.js b/skills/chrome-control/chunks/effect-services-BVazUC0E.js new file mode 100644 index 0000000..1012110 --- /dev/null +++ b/skills/chrome-control/chunks/effect-services-BVazUC0E.js @@ -0,0 +1,171 @@ +const require_Layer = require("./Layer-nAKmzBoW.js"); +let node_fs = require("node:fs"); +node_fs = require_Layer.__toESM(node_fs); +let node_child_process = require("node:child_process"); +node_child_process = require_Layer.__toESM(node_child_process); +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/FiberRef.js +/** +* @since 2.0.0 +* @category fiberRefs +*/ +var currentLoggers = require_Layer.currentLoggers; +//#endregion +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Logger.js +/** +* @since 2.0.0 +* @category constructors +*/ +var defaultLogger = require_Layer.defaultLogger; +/** +* A default version of the pretty logger. +* +* @since 3.8.0 +* @category constructors +*/ +var prettyLoggerDefault = require_Layer.prettyLoggerDefault; +//#endregion +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/@effect+platform@0.96.1_effect@3.21.2/node_modules/@effect/platform/dist/esm/Runtime.js +/** +* @since 1.0.0 +*/ +/** +* @category teardown +* @since 1.0.0 +*/ +var defaultTeardown = (exit, onExit) => { + onExit(require_Layer.isFailure(exit) && !require_Layer.isInterruptedOnly(exit.cause) ? 1 : 0); +}; +var addPrettyLogger = (refs, fiberId) => { + const loggers = require_Layer.getOrDefault(refs, currentLoggers); + if (!require_Layer.has(loggers, defaultLogger)) return refs; + return require_Layer.updateAs(refs, { + fiberId, + fiberRef: currentLoggers, + value: loggers.pipe(require_Layer.remove(defaultLogger), require_Layer.add(prettyLoggerDefault)) + }); +}; +/** +* @category constructors +* @since 1.0.0 +*/ +var makeRunMain = (f) => require_Layer.dual((args) => require_Layer.isEffect(args[0]), (effect, options) => { + return f({ + fiber: options?.disableErrorReporting === true ? require_Layer.runFork(effect, { updateRefs: options?.disablePrettyLogger === true ? void 0 : addPrettyLogger }) : require_Layer.runFork(require_Layer.tapErrorCause(effect, (cause) => { + if (require_Layer.isInterruptedOnly(cause)) return require_Layer._void; + return require_Layer.logError(cause); + }), { updateRefs: options?.disablePrettyLogger === true ? void 0 : addPrettyLogger }), + teardown: options?.teardown ?? defaultTeardown + }); +}); +//#endregion +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/@effect+platform-node@0.106.0_@effect+cluster@0.58.2_@effect+platform@0.96.1_effect@3.2_a80e93d5c9b0cafd3a2a57951ab69ada/node_modules/@effect/platform-node/dist/esm/NodeRuntime.js +/** +* @since 1.0.0 +*/ +/** +* @since 1.0.0 +* @category runtime +*/ +var runMain = /* @__PURE__ */ makeRunMain(({ fiber, teardown }) => { + const keepAlive = setInterval(require_Layer.constVoid, 2 ** 31 - 1); + let receivedSignal = false; + fiber.addObserver((exit) => { + if (!receivedSignal) { + process.removeListener("SIGINT", onSigint); + process.removeListener("SIGTERM", onSigint); + } + clearInterval(keepAlive); + teardown(exit, (code) => { + if (receivedSignal || code !== 0) process.exit(code); + }); + }); + function onSigint() { + receivedSignal = true; + process.removeListener("SIGINT", onSigint); + process.removeListener("SIGTERM", onSigint); + fiber.unsafeInterruptAsFork(fiber.id()); + } + process.on("SIGINT", onSigint); + process.on("SIGTERM", onSigint); +}); +//#endregion +//#region src/scripts/effect-services.ts +var ScriptIo = class extends require_Layer.Tag("opzero/ScriptIo")() {}; +function toError(error) { + return error instanceof Error ? error : new Error(String(error || "Unknown error")); +} +var ScriptIoLive = require_Layer.succeed(ScriptIo, { + exists: (file) => require_Layer.sync(() => node_fs.default.existsSync(file)), + readText: (file) => require_Layer.try_({ + try: () => node_fs.default.readFileSync(file, "utf8"), + catch: toError + }), + writeText: (file, text) => require_Layer.try_({ + try: () => { + node_fs.default.writeFileSync(file, text); + }, + catch: toError + }), + mkdir: (dir) => require_Layer.try_({ + try: () => { + node_fs.default.mkdirSync(dir, { recursive: true }); + }, + catch: toError + }), + chmod: (file, mode) => require_Layer.try_({ + try: () => { + node_fs.default.chmodSync(file, mode); + }, + catch: toError + }), + readdir: (dir) => require_Layer.try_({ + try: () => node_fs.default.readdirSync(dir), + catch: toError + }), + execFile: (command, args, options) => require_Layer.try_({ + try: () => String(node_child_process.default.execFileSync(command, args, options)), + catch: toError + }), + execFileInherit: (command, args) => require_Layer.try_({ + try: () => { + node_child_process.default.execFileSync(command, args, { stdio: "inherit" }); + }, + catch: toError + }), + stdout: (text) => require_Layer.sync(() => { + process.stdout.write(text); + }), + stderr: (text) => require_Layer.sync(() => { + process.stderr.write(text); + }) +}); +function runScript(program) { + runMain(require_Layer.provide(program, ScriptIoLive)); +} +function argValue(name, fallback) { + const prefix = `--${name}=`; + const direct = process.argv.find((arg) => arg.startsWith(prefix)); + if (direct) return direct.slice(prefix.length); + const index = process.argv.indexOf(`--${name}`); + if (index !== -1) return process.argv[index + 1]; + return fallback || null; +} +//#endregion +Object.defineProperty(exports, "ScriptIo", { + enumerable: true, + get: function() { + return ScriptIo; + } +}); +Object.defineProperty(exports, "argValue", { + enumerable: true, + get: function() { + return argValue; + } +}); +Object.defineProperty(exports, "runScript", { + enumerable: true, + get: function() { + return runScript; + } +}); diff --git a/skills/chrome-control/chunks/effect-services-BrlJlqgg.js b/skills/chrome-control/chunks/effect-services-BrlJlqgg.js deleted file mode 100644 index 8c57afd..0000000 --- a/skills/chrome-control/chunks/effect-services-BrlJlqgg.js +++ /dev/null @@ -1,85 +0,0 @@ -const require_NodeRuntime = require("./NodeRuntime-CfddDOOs.js"); -let node_fs = require("node:fs"); -node_fs = require_NodeRuntime.__toESM(node_fs); -let node_child_process = require("node:child_process"); -node_child_process = require_NodeRuntime.__toESM(node_child_process); -//#region src/scripts/effect-services.ts -var ScriptIo = class extends require_NodeRuntime.Tag("opzero/ScriptIo")() {}; -function toError(error) { - return error instanceof Error ? error : new Error(String(error || "Unknown error")); -} -var ScriptIoLive = require_NodeRuntime.succeed(ScriptIo, { - exists: (file) => require_NodeRuntime.sync(() => node_fs.default.existsSync(file)), - readText: (file) => require_NodeRuntime.try_({ - try: () => node_fs.default.readFileSync(file, "utf8"), - catch: toError - }), - writeText: (file, text) => require_NodeRuntime.try_({ - try: () => { - node_fs.default.writeFileSync(file, text); - }, - catch: toError - }), - mkdir: (dir) => require_NodeRuntime.try_({ - try: () => { - node_fs.default.mkdirSync(dir, { recursive: true }); - }, - catch: toError - }), - chmod: (file, mode) => require_NodeRuntime.try_({ - try: () => { - node_fs.default.chmodSync(file, mode); - }, - catch: toError - }), - readdir: (dir) => require_NodeRuntime.try_({ - try: () => node_fs.default.readdirSync(dir), - catch: toError - }), - execFile: (command, args, options) => require_NodeRuntime.try_({ - try: () => String(node_child_process.default.execFileSync(command, args, options)), - catch: toError - }), - execFileInherit: (command, args) => require_NodeRuntime.try_({ - try: () => { - node_child_process.default.execFileSync(command, args, { stdio: "inherit" }); - }, - catch: toError - }), - stdout: (text) => require_NodeRuntime.sync(() => { - process.stdout.write(text); - }), - stderr: (text) => require_NodeRuntime.sync(() => { - process.stderr.write(text); - }) -}); -function runScript(program) { - require_NodeRuntime.runMain(require_NodeRuntime.provide(program, ScriptIoLive)); -} -function argValue(name, fallback) { - const prefix = `--${name}=`; - const direct = process.argv.find((arg) => arg.startsWith(prefix)); - if (direct) return direct.slice(prefix.length); - const index = process.argv.indexOf(`--${name}`); - if (index !== -1) return process.argv[index + 1]; - return fallback || null; -} -//#endregion -Object.defineProperty(exports, "ScriptIo", { - enumerable: true, - get: function() { - return ScriptIo; - } -}); -Object.defineProperty(exports, "argValue", { - enumerable: true, - get: function() { - return argValue; - } -}); -Object.defineProperty(exports, "runScript", { - enumerable: true, - get: function() { - return runScript; - } -}); diff --git a/skills/chrome-control/chunks/rpc-BTiTvO3J.js b/skills/chrome-control/chunks/rpc-BTiTvO3J.js new file mode 100644 index 0000000..66b44ac --- /dev/null +++ b/skills/chrome-control/chunks/rpc-BTiTvO3J.js @@ -0,0 +1,5403 @@ +const require_Layer = require("./Layer-nAKmzBoW.js"); +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/schema/util.js +/** @internal */ +var getKeysForIndexSignature = (input, parameter) => { + switch (parameter._tag) { + case "StringKeyword": + case "TemplateLiteral": return Object.keys(input); + case "SymbolKeyword": return Object.getOwnPropertySymbols(input); + case "Refinement": return getKeysForIndexSignature(input, parameter.from); + } +}; +/** @internal */ +var memoizeThunk = (f) => { + let done = false; + let a; + return () => { + if (done) return a; + a = f(); + done = true; + return a; + }; +}; +/** @internal */ +var isNonEmpty$1 = (x) => Array.isArray(x); +/** @internal */ +var isSingle = (x) => !Array.isArray(x); +/** @internal */ +var formatPathKey = (key) => `[${require_Layer.formatPropertyKey(key)}]`; +/** @internal */ +var formatPath = (path) => isNonEmpty$1(path) ? path.map(formatPathKey).join("") : formatPathKey(path); +//#endregion +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/schema/errors.js +var getErrorMessage$1 = (reason, details, path, ast) => { + let out = reason; + if (path && require_Layer.isNonEmptyReadonlyArray(path)) out += `\nat path: ${formatPath(path)}`; + if (details !== void 0) out += `\ndetails: ${details}`; + if (ast) out += `\nschema (${ast._tag}): ${ast}`; + return out; +}; +/** @internal */ +var getASTUnsupportedKeySchemaErrorMessage = (ast) => getErrorMessage$1("Unsupported key schema", void 0, void 0, ast); +/** @internal */ +var getASTUnsupportedLiteralErrorMessage = (literal) => getErrorMessage$1("Unsupported literal", `literal value: ${require_Layer.formatUnknown(literal)}`); +/** @internal */ +var getASTDuplicateIndexSignatureErrorMessage = (type) => getErrorMessage$1("Duplicate index signature", `${type} index signature`); +/** @internal */ +var getASTIndexSignatureParameterErrorMessage = /* @__PURE__ */ getErrorMessage$1("Unsupported index signature parameter", "An index signature parameter type must be `string`, `symbol`, a template literal type or a refinement of the previous types"); +/** @internal */ +var getASTRequiredElementFollowinAnOptionalElementErrorMessage = /* @__PURE__ */ getErrorMessage$1("Invalid element", "A required element cannot follow an optional element. ts(1257)"); +/** @internal */ +var getASTDuplicatePropertySignatureTransformationErrorMessage = (key) => getErrorMessage$1("Duplicate property signature transformation", `Duplicate key ${require_Layer.formatUnknown(key)}`); +/** @internal */ +var getASTDuplicatePropertySignatureErrorMessage = (key) => getErrorMessage$1("Duplicate property signature", `Duplicate key ${require_Layer.formatUnknown(key)}`); +//#endregion +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/schema/schemaId.js +/** @internal */ +var DateFromSelfSchemaId$1 = /* @__PURE__ */ Symbol.for("effect/SchemaId/DateFromSelf"); +/** @internal */ +var GreaterThanSchemaId$1 = /* @__PURE__ */ Symbol.for("effect/SchemaId/GreaterThan"); +/** @internal */ +var GreaterThanOrEqualToSchemaId$1 = /* @__PURE__ */ Symbol.for("effect/SchemaId/GreaterThanOrEqualTo"); +/** @internal */ +var LessThanSchemaId$1 = /* @__PURE__ */ Symbol.for("effect/SchemaId/LessThan"); +/** @internal */ +var LessThanOrEqualToSchemaId$1 = /* @__PURE__ */ Symbol.for("effect/SchemaId/LessThanOrEqualTo"); +/** @internal */ +var IntSchemaId$1 = /* @__PURE__ */ Symbol.for("effect/SchemaId/Int"); +/** @internal */ +var NonNaNSchemaId$1 = /* @__PURE__ */ Symbol.for("effect/SchemaId/NonNaN"); +/** @internal */ +var FiniteSchemaId$1 = /* @__PURE__ */ Symbol.for("effect/SchemaId/Finite"); +/** @internal */ +var JsonNumberSchemaId$1 = /* @__PURE__ */ Symbol.for("effect/SchemaId/JsonNumber"); +/** @internal */ +var BetweenSchemaId$1 = /* @__PURE__ */ Symbol.for("effect/SchemaId/Between"); +/** @internal */ +var GreaterThanOrEqualToBigIntSchemaId$1 = /* @__PURE__ */ Symbol.for("effect/SchemaId/GreaterThanOrEqualToBigint"); +/** @internal */ +var BetweenBigintSchemaId = /* @__PURE__ */ Symbol.for("effect/SchemaId/BetweenBigint"); +/** @internal */ +var MinLengthSchemaId$1 = /* @__PURE__ */ Symbol.for("effect/SchemaId/MinLength"); +/** @internal */ +var LengthSchemaId$1 = /* @__PURE__ */ Symbol.for("effect/SchemaId/Length"); +//#endregion +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/SchemaAST.js +/** +* @since 3.10.0 +*/ +/** +* @category annotations +* @since 3.19.0 +* @experimental +*/ +var TypeConstructorAnnotationId = /* @__PURE__ */ Symbol.for("effect/annotation/TypeConstructor"); +/** +* @category annotations +* @since 3.10.0 +*/ +var BrandAnnotationId = /* @__PURE__ */ Symbol.for("effect/annotation/Brand"); +/** +* @category annotations +* @since 3.10.0 +*/ +var SchemaIdAnnotationId = /* @__PURE__ */ Symbol.for("effect/annotation/SchemaId"); +/** +* @category annotations +* @since 3.10.0 +*/ +var MessageAnnotationId = /* @__PURE__ */ Symbol.for("effect/annotation/Message"); +/** +* @category annotations +* @since 3.10.0 +*/ +var MissingMessageAnnotationId = /* @__PURE__ */ Symbol.for("effect/annotation/MissingMessage"); +/** +* @category annotations +* @since 3.10.0 +*/ +var IdentifierAnnotationId = /* @__PURE__ */ Symbol.for("effect/annotation/Identifier"); +/** +* @category annotations +* @since 3.10.0 +*/ +var TitleAnnotationId = /* @__PURE__ */ Symbol.for("effect/annotation/Title"); +/** @internal */ +var AutoTitleAnnotationId = /* @__PURE__ */ Symbol.for("effect/annotation/AutoTitle"); +/** +* @category annotations +* @since 3.10.0 +*/ +var DescriptionAnnotationId = /* @__PURE__ */ Symbol.for("effect/annotation/Description"); +/** +* @category annotations +* @since 3.10.0 +*/ +var ExamplesAnnotationId = /* @__PURE__ */ Symbol.for("effect/annotation/Examples"); +/** +* @category annotations +* @since 3.10.0 +*/ +var DefaultAnnotationId = /* @__PURE__ */ Symbol.for("effect/annotation/Default"); +/** +* @category annotations +* @since 3.10.0 +*/ +var JSONSchemaAnnotationId = /* @__PURE__ */ Symbol.for("effect/annotation/JSONSchema"); +/** +* @category annotations +* @since 3.10.0 +*/ +var ArbitraryAnnotationId = /* @__PURE__ */ Symbol.for("effect/annotation/Arbitrary"); +/** +* @category annotations +* @since 3.10.0 +*/ +var PrettyAnnotationId = /* @__PURE__ */ Symbol.for("effect/annotation/Pretty"); +/** +* @category annotations +* @since 3.10.0 +*/ +var EquivalenceAnnotationId = /* @__PURE__ */ Symbol.for("effect/annotation/Equivalence"); +/** +* @category annotations +* @since 3.10.0 +*/ +var DocumentationAnnotationId = /* @__PURE__ */ Symbol.for("effect/annotation/Documentation"); +/** +* @category annotations +* @since 3.10.0 +*/ +var ConcurrencyAnnotationId = /* @__PURE__ */ Symbol.for("effect/annotation/Concurrency"); +/** +* @category annotations +* @since 3.10.0 +*/ +var BatchingAnnotationId = /* @__PURE__ */ Symbol.for("effect/annotation/Batching"); +/** +* @category annotations +* @since 3.10.0 +*/ +var ParseIssueTitleAnnotationId = /* @__PURE__ */ Symbol.for("effect/annotation/ParseIssueTitle"); +/** +* @category annotations +* @since 3.10.0 +*/ +var ParseOptionsAnnotationId = /* @__PURE__ */ Symbol.for("effect/annotation/ParseOptions"); +/** +* @category annotations +* @since 3.10.0 +*/ +var DecodingFallbackAnnotationId = /* @__PURE__ */ Symbol.for("effect/annotation/DecodingFallback"); +/** +* @category annotations +* @since 3.10.0 +*/ +var SurrogateAnnotationId = /* @__PURE__ */ Symbol.for("effect/annotation/Surrogate"); +/** @internal */ +var StableFilterAnnotationId = /* @__PURE__ */ Symbol.for("effect/annotation/StableFilter"); +/** +* @category annotations +* @since 3.10.0 +*/ +var getAnnotation = /* @__PURE__ */ require_Layer.dual(2, (annotated, key) => Object.prototype.hasOwnProperty.call(annotated.annotations, key) ? require_Layer.some(annotated.annotations[key]) : require_Layer.none$1()); +/** +* @category annotations +* @since 3.10.0 +*/ +var getBrandAnnotation = /* @__PURE__ */ getAnnotation(BrandAnnotationId); +/** +* @category annotations +* @since 3.10.0 +*/ +var getMessageAnnotation = /* @__PURE__ */ getAnnotation(MessageAnnotationId); +/** +* @category annotations +* @since 3.10.0 +*/ +var getMissingMessageAnnotation = /* @__PURE__ */ getAnnotation(MissingMessageAnnotationId); +/** +* @category annotations +* @since 3.10.0 +*/ +var getTitleAnnotation = /* @__PURE__ */ getAnnotation(TitleAnnotationId); +/** @internal */ +var getAutoTitleAnnotation = /* @__PURE__ */ getAnnotation(AutoTitleAnnotationId); +/** +* @category annotations +* @since 3.10.0 +*/ +var getIdentifierAnnotation = /* @__PURE__ */ getAnnotation(IdentifierAnnotationId); +/** +* @category annotations +* @since 3.10.0 +*/ +var getDescriptionAnnotation = /* @__PURE__ */ getAnnotation(DescriptionAnnotationId); +/** +* @category annotations +* @since 3.10.0 +*/ +var getConcurrencyAnnotation = /* @__PURE__ */ getAnnotation(ConcurrencyAnnotationId); +/** +* @category annotations +* @since 3.10.0 +*/ +var getBatchingAnnotation = /* @__PURE__ */ getAnnotation(BatchingAnnotationId); +/** +* @category annotations +* @since 3.10.0 +*/ +var getParseIssueTitleAnnotation$1 = /* @__PURE__ */ getAnnotation(ParseIssueTitleAnnotationId); +/** +* @category annotations +* @since 3.10.0 +*/ +var getParseOptionsAnnotation = /* @__PURE__ */ getAnnotation(ParseOptionsAnnotationId); +/** +* @category annotations +* @since 3.10.0 +*/ +var getDecodingFallbackAnnotation = /* @__PURE__ */ getAnnotation(DecodingFallbackAnnotationId); +/** +* @category annotations +* @since 3.10.0 +*/ +var getSurrogateAnnotation = /* @__PURE__ */ getAnnotation(SurrogateAnnotationId); +var getStableFilterAnnotation = /* @__PURE__ */ getAnnotation(StableFilterAnnotationId); +/** @internal */ +var hasStableFilter = (annotated) => require_Layer.exists(getStableFilterAnnotation(annotated), (b) => b === true); +/** +* @category annotations +* @since 3.10.0 +*/ +var JSONIdentifierAnnotationId = /* @__PURE__ */ Symbol.for("effect/annotation/JSONIdentifier"); +/** +* @category annotations +* @since 3.10.0 +*/ +var getJSONIdentifierAnnotation = /* @__PURE__ */ getAnnotation(JSONIdentifierAnnotationId); +/** +* @category annotations +* @since 3.10.0 +*/ +var getJSONIdentifier = (annotated) => require_Layer.orElse(getJSONIdentifierAnnotation(annotated), () => getIdentifierAnnotation(annotated)); +/** +* @category model +* @since 3.10.0 +*/ +var Declaration = class { + typeParameters; + decodeUnknown; + encodeUnknown; + annotations; + /** + * @since 3.10.0 + */ + _tag = "Declaration"; + constructor(typeParameters, decodeUnknown, encodeUnknown, annotations = {}) { + this.typeParameters = typeParameters; + this.decodeUnknown = decodeUnknown; + this.encodeUnknown = encodeUnknown; + this.annotations = annotations; + } + /** + * @since 3.10.0 + */ + toString() { + return require_Layer.getOrElse(getExpected(this), () => ""); + } + /** + * @since 3.10.0 + */ + toJSON() { + return { + _tag: this._tag, + typeParameters: this.typeParameters.map((ast) => ast.toJSON()), + annotations: toJSONAnnotations(this.annotations) + }; + } +}; +var createASTGuard = (tag) => (ast) => ast._tag === tag; +/** +* @category model +* @since 3.10.0 +*/ +var Literal$1 = class { + literal; + annotations; + /** + * @since 3.10.0 + */ + _tag = "Literal"; + constructor(literal, annotations = {}) { + this.literal = literal; + this.annotations = annotations; + } + /** + * @since 3.10.0 + */ + toString() { + return require_Layer.getOrElse(getExpected(this), () => require_Layer.formatUnknown(this.literal)); + } + /** + * @since 3.10.0 + */ + toJSON() { + return { + _tag: this._tag, + literal: require_Layer.isBigInt(this.literal) ? String(this.literal) : this.literal, + annotations: toJSONAnnotations(this.annotations) + }; + } +}; +/** +* @category guards +* @since 3.10.0 +*/ +var isLiteral = /* @__PURE__ */ createASTGuard("Literal"); +var $null = /* @__PURE__ */ new Literal$1(null); +/** +* @category model +* @since 3.10.0 +*/ +var UniqueSymbol = class { + symbol; + annotations; + /** + * @since 3.10.0 + */ + _tag = "UniqueSymbol"; + constructor(symbol, annotations = {}) { + this.symbol = symbol; + this.annotations = annotations; + } + /** + * @since 3.10.0 + */ + toString() { + return require_Layer.getOrElse(getExpected(this), () => require_Layer.formatUnknown(this.symbol)); + } + /** + * @since 3.10.0 + */ + toJSON() { + return { + _tag: this._tag, + symbol: String(this.symbol), + annotations: toJSONAnnotations(this.annotations) + }; + } +}; +/** +* @category model +* @since 3.10.0 +*/ +var UndefinedKeyword = class { + annotations; + /** + * @since 3.10.0 + */ + _tag = "UndefinedKeyword"; + constructor(annotations = {}) { + this.annotations = annotations; + } + /** + * @since 3.10.0 + */ + toString() { + return formatKeyword(this); + } + /** + * @since 3.10.0 + */ + toJSON() { + return { + _tag: this._tag, + annotations: toJSONAnnotations(this.annotations) + }; + } +}; +/** +* @category constructors +* @since 3.10.0 +*/ +var undefinedKeyword = /* @__PURE__ */ new UndefinedKeyword({ [TitleAnnotationId]: "undefined" }); +/** +* @category model +* @since 3.10.0 +*/ +var NeverKeyword = class { + annotations; + /** + * @since 3.10.0 + */ + _tag = "NeverKeyword"; + constructor(annotations = {}) { + this.annotations = annotations; + } + /** + * @since 3.10.0 + */ + toString() { + return formatKeyword(this); + } + /** + * @since 3.10.0 + */ + toJSON() { + return { + _tag: this._tag, + annotations: toJSONAnnotations(this.annotations) + }; + } +}; +/** +* @category constructors +* @since 3.10.0 +*/ +var neverKeyword = /* @__PURE__ */ new NeverKeyword({ [TitleAnnotationId]: "never" }); +/** +* @category model +* @since 3.10.0 +*/ +var UnknownKeyword = class { + annotations; + /** + * @since 3.10.0 + */ + _tag = "UnknownKeyword"; + constructor(annotations = {}) { + this.annotations = annotations; + } + /** + * @since 3.10.0 + */ + toString() { + return formatKeyword(this); + } + /** + * @since 3.10.0 + */ + toJSON() { + return { + _tag: this._tag, + annotations: toJSONAnnotations(this.annotations) + }; + } +}; +/** +* @category constructors +* @since 3.10.0 +*/ +var unknownKeyword = /* @__PURE__ */ new UnknownKeyword({ [TitleAnnotationId]: "unknown" }); +/** +* @category model +* @since 3.10.0 +*/ +var AnyKeyword = class { + annotations; + /** + * @since 3.10.0 + */ + _tag = "AnyKeyword"; + constructor(annotations = {}) { + this.annotations = annotations; + } + /** + * @since 3.10.0 + */ + toString() { + return formatKeyword(this); + } + /** + * @since 3.10.0 + */ + toJSON() { + return { + _tag: this._tag, + annotations: toJSONAnnotations(this.annotations) + }; + } +}; +/** +* @category constructors +* @since 3.10.0 +*/ +var anyKeyword = /* @__PURE__ */ new AnyKeyword({ [TitleAnnotationId]: "any" }); +/** +* @category model +* @since 3.10.0 +*/ +var StringKeyword = class { + annotations; + /** + * @since 3.10.0 + */ + _tag = "StringKeyword"; + constructor(annotations = {}) { + this.annotations = annotations; + } + /** + * @since 3.10.0 + */ + toString() { + return formatKeyword(this); + } + /** + * @since 3.10.0 + */ + toJSON() { + return { + _tag: this._tag, + annotations: toJSONAnnotations(this.annotations) + }; + } +}; +/** +* @category constructors +* @since 3.10.0 +*/ +var stringKeyword = /* @__PURE__ */ new StringKeyword({ + [TitleAnnotationId]: "string", + [DescriptionAnnotationId]: "a string" +}); +/** +* @category guards +* @since 3.10.0 +*/ +var isStringKeyword = /* @__PURE__ */ createASTGuard("StringKeyword"); +/** +* @category model +* @since 3.10.0 +*/ +var NumberKeyword = class { + annotations; + /** + * @since 3.10.0 + */ + _tag = "NumberKeyword"; + constructor(annotations = {}) { + this.annotations = annotations; + } + /** + * @since 3.10.0 + */ + toString() { + return formatKeyword(this); + } + /** + * @since 3.10.0 + */ + toJSON() { + return { + _tag: this._tag, + annotations: toJSONAnnotations(this.annotations) + }; + } +}; +/** +* @category constructors +* @since 3.10.0 +*/ +var numberKeyword = /* @__PURE__ */ new NumberKeyword({ + [TitleAnnotationId]: "number", + [DescriptionAnnotationId]: "a number" +}); +/** +* @category model +* @since 3.10.0 +*/ +var BooleanKeyword = class { + annotations; + /** + * @since 3.10.0 + */ + _tag = "BooleanKeyword"; + constructor(annotations = {}) { + this.annotations = annotations; + } + /** + * @since 3.10.0 + */ + toString() { + return formatKeyword(this); + } + /** + * @since 3.10.0 + */ + toJSON() { + return { + _tag: this._tag, + annotations: toJSONAnnotations(this.annotations) + }; + } +}; +/** +* @category constructors +* @since 3.10.0 +*/ +var booleanKeyword = /* @__PURE__ */ new BooleanKeyword({ + [TitleAnnotationId]: "boolean", + [DescriptionAnnotationId]: "a boolean" +}); +/** +* @category model +* @since 3.10.0 +*/ +var BigIntKeyword = class { + annotations; + /** + * @since 3.10.0 + */ + _tag = "BigIntKeyword"; + constructor(annotations = {}) { + this.annotations = annotations; + } + /** + * @since 3.10.0 + */ + toString() { + return formatKeyword(this); + } + /** + * @since 3.10.0 + */ + toJSON() { + return { + _tag: this._tag, + annotations: toJSONAnnotations(this.annotations) + }; + } +}; +/** +* @category constructors +* @since 3.10.0 +*/ +var bigIntKeyword = /* @__PURE__ */ new BigIntKeyword({ + [TitleAnnotationId]: "bigint", + [DescriptionAnnotationId]: "a bigint" +}); +/** +* @category model +* @since 3.10.0 +*/ +var SymbolKeyword = class { + annotations; + /** + * @since 3.10.0 + */ + _tag = "SymbolKeyword"; + constructor(annotations = {}) { + this.annotations = annotations; + } + /** + * @since 3.10.0 + */ + toString() { + return formatKeyword(this); + } + /** + * @since 3.10.0 + */ + toJSON() { + return { + _tag: this._tag, + annotations: toJSONAnnotations(this.annotations) + }; + } +}; +/** +* @category constructors +* @since 3.10.0 +*/ +var symbolKeyword = /* @__PURE__ */ new SymbolKeyword({ + [TitleAnnotationId]: "symbol", + [DescriptionAnnotationId]: "a symbol" +}); +/** +* @category guards +* @since 3.10.0 +*/ +var isSymbolKeyword = /* @__PURE__ */ createASTGuard("SymbolKeyword"); +/** +* @category model +* @since 3.10.0 +*/ +var Type$1 = class { + type; + annotations; + constructor(type, annotations = {}) { + this.type = type; + this.annotations = annotations; + } + /** + * @since 3.10.0 + */ + toJSON() { + return { + type: this.type.toJSON(), + annotations: toJSONAnnotations(this.annotations) + }; + } + /** + * @since 3.10.0 + */ + toString() { + return String(this.type); + } +}; +/** +* @category model +* @since 3.10.0 +*/ +var OptionalType = class extends Type$1 { + isOptional; + constructor(type, isOptional, annotations = {}) { + super(type, annotations); + this.isOptional = isOptional; + } + /** + * @since 3.10.0 + */ + toJSON() { + return { + type: this.type.toJSON(), + isOptional: this.isOptional, + annotations: toJSONAnnotations(this.annotations) + }; + } + /** + * @since 3.10.0 + */ + toString() { + return String(this.type) + (this.isOptional ? "?" : ""); + } +}; +var getRestASTs = (rest) => rest.map((annotatedAST) => annotatedAST.type); +/** +* @category model +* @since 3.10.0 +*/ +var TupleType = class { + elements; + rest; + isReadonly; + annotations; + /** + * @since 3.10.0 + */ + _tag = "TupleType"; + constructor(elements, rest, isReadonly, annotations = {}) { + this.elements = elements; + this.rest = rest; + this.isReadonly = isReadonly; + this.annotations = annotations; + let hasOptionalElement = false; + let hasIllegalRequiredElement = false; + for (const e of elements) if (e.isOptional) hasOptionalElement = true; + else if (hasOptionalElement) { + hasIllegalRequiredElement = true; + break; + } + if (hasIllegalRequiredElement || hasOptionalElement && rest.length > 1) throw new Error(getASTRequiredElementFollowinAnOptionalElementErrorMessage); + } + /** + * @since 3.10.0 + */ + toString() { + return require_Layer.getOrElse(getExpected(this), () => formatTuple(this)); + } + /** + * @since 3.10.0 + */ + toJSON() { + return { + _tag: this._tag, + elements: this.elements.map((e) => e.toJSON()), + rest: this.rest.map((ast) => ast.toJSON()), + isReadonly: this.isReadonly, + annotations: toJSONAnnotations(this.annotations) + }; + } +}; +var formatTuple = (ast) => { + const formattedElements = ast.elements.map(String).join(", "); + return require_Layer.matchLeft(ast.rest, { + onEmpty: () => `readonly [${formattedElements}]`, + onNonEmpty: (head, tail) => { + const formattedHead = String(head); + const wrappedHead = formattedHead.includes(" | ") ? `(${formattedHead})` : formattedHead; + if (tail.length > 0) { + const formattedTail = tail.map(String).join(", "); + if (ast.elements.length > 0) return `readonly [${formattedElements}, ...${wrappedHead}[], ${formattedTail}]`; + else return `readonly [...${wrappedHead}[], ${formattedTail}]`; + } else if (ast.elements.length > 0) return `readonly [${formattedElements}, ...${wrappedHead}[]]`; + else return `ReadonlyArray<${formattedHead}>`; + } + }); +}; +/** +* @category model +* @since 3.10.0 +*/ +var PropertySignature = class extends OptionalType { + name; + isReadonly; + constructor(name, type, isOptional, isReadonly, annotations) { + super(type, isOptional, annotations); + this.name = name; + this.isReadonly = isReadonly; + } + /** + * @since 3.10.0 + */ + toString() { + return (this.isReadonly ? "readonly " : "") + String(this.name) + (this.isOptional ? "?" : "") + ": " + this.type; + } + /** + * @since 3.10.0 + */ + toJSON() { + return { + name: String(this.name), + type: this.type.toJSON(), + isOptional: this.isOptional, + isReadonly: this.isReadonly, + annotations: toJSONAnnotations(this.annotations) + }; + } +}; +/** +* @since 3.10.0 +*/ +var isParameter = (ast) => { + switch (ast._tag) { + case "StringKeyword": + case "SymbolKeyword": + case "TemplateLiteral": return true; + case "Refinement": return isParameter(ast.from); + } + return false; +}; +/** +* @category model +* @since 3.10.0 +*/ +var IndexSignature = class { + type; + isReadonly; + /** + * @since 3.10.0 + */ + parameter; + constructor(parameter, type, isReadonly) { + this.type = type; + this.isReadonly = isReadonly; + if (isParameter(parameter)) this.parameter = parameter; + else throw new Error(getASTIndexSignatureParameterErrorMessage); + } + /** + * @since 3.10.0 + */ + toString() { + return (this.isReadonly ? "readonly " : "") + `[x: ${this.parameter}]: ${this.type}`; + } + /** + * @since 3.10.0 + */ + toJSON() { + return { + parameter: this.parameter.toJSON(), + type: this.type.toJSON(), + isReadonly: this.isReadonly + }; + } +}; +/** +* @category model +* @since 3.10.0 +*/ +var TypeLiteral = class { + annotations; + /** + * @since 3.10.0 + */ + _tag = "TypeLiteral"; + /** + * @since 3.10.0 + */ + propertySignatures; + /** + * @since 3.10.0 + */ + indexSignatures; + constructor(propertySignatures, indexSignatures, annotations = {}) { + this.annotations = annotations; + const keys = {}; + for (let i = 0; i < propertySignatures.length; i++) { + const name = propertySignatures[i].name; + if (Object.prototype.hasOwnProperty.call(keys, name)) throw new Error(getASTDuplicatePropertySignatureErrorMessage(name)); + keys[name] = null; + } + const parameters = { + string: false, + symbol: false + }; + for (let i = 0; i < indexSignatures.length; i++) { + const encodedParameter = getEncodedParameter(indexSignatures[i].parameter); + if (isStringKeyword(encodedParameter)) { + if (parameters.string) throw new Error(getASTDuplicateIndexSignatureErrorMessage("string")); + parameters.string = true; + } else if (isSymbolKeyword(encodedParameter)) { + if (parameters.symbol) throw new Error(getASTDuplicateIndexSignatureErrorMessage("symbol")); + parameters.symbol = true; + } + } + this.propertySignatures = propertySignatures; + this.indexSignatures = indexSignatures; + } + /** + * @since 3.10.0 + */ + toString() { + return require_Layer.getOrElse(getExpected(this), () => formatTypeLiteral(this)); + } + /** + * @since 3.10.0 + */ + toJSON() { + return { + _tag: this._tag, + propertySignatures: this.propertySignatures.map((ps) => ps.toJSON()), + indexSignatures: this.indexSignatures.map((ps) => ps.toJSON()), + annotations: toJSONAnnotations(this.annotations) + }; + } +}; +var formatIndexSignatures = (iss) => iss.map(String).join("; "); +var formatTypeLiteral = (ast) => { + if (ast.propertySignatures.length > 0) { + const pss = ast.propertySignatures.map(String).join("; "); + if (ast.indexSignatures.length > 0) return `{ ${pss}; ${formatIndexSignatures(ast.indexSignatures)} }`; + else return `{ ${pss} }`; + } else if (ast.indexSignatures.length > 0) return `{ ${formatIndexSignatures(ast.indexSignatures)} }`; + else return "{}"; +}; +var sortCandidates = /* @__PURE__ */ require_Layer.sort(/* @__PURE__ */ require_Layer.mapInput(require_Layer.Order, (ast) => { + switch (ast._tag) { + case "AnyKeyword": return 0; + case "UnknownKeyword": return 1; + case "ObjectKeyword": return 2; + case "StringKeyword": + case "NumberKeyword": + case "BooleanKeyword": + case "BigIntKeyword": + case "SymbolKeyword": return 3; + } + return 4; +})); +var literalMap = { + string: "StringKeyword", + number: "NumberKeyword", + boolean: "BooleanKeyword", + bigint: "BigIntKeyword" +}; +/** @internal */ +var flatten = (candidates) => require_Layer.flatMap$1(candidates, (ast) => isUnion(ast) ? flatten(ast.types) : [ast]); +/** @internal */ +var unify = (candidates) => { + const cs = sortCandidates(candidates); + const out = []; + const uniques = {}; + const literals = []; + for (const ast of cs) switch (ast._tag) { + case "NeverKeyword": break; + case "AnyKeyword": return [anyKeyword]; + case "UnknownKeyword": return [unknownKeyword]; + case "ObjectKeyword": + case "UndefinedKeyword": + case "VoidKeyword": + case "StringKeyword": + case "NumberKeyword": + case "BooleanKeyword": + case "BigIntKeyword": + case "SymbolKeyword": + if (!uniques[ast._tag]) { + uniques[ast._tag] = ast; + out.push(ast); + } + break; + case "Literal": { + const type = typeof ast.literal; + switch (type) { + case "string": + case "number": + case "bigint": + case "boolean": + if (!uniques[literalMap[type]] && !literals.includes(ast.literal)) { + literals.push(ast.literal); + out.push(ast); + } + break; + case "object": + if (!literals.includes(ast.literal)) { + literals.push(ast.literal); + out.push(ast); + } + break; + } + break; + } + case "UniqueSymbol": + if (!uniques["SymbolKeyword"] && !literals.includes(ast.symbol)) { + literals.push(ast.symbol); + out.push(ast); + } + break; + case "TupleType": + if (!uniques["ObjectKeyword"]) out.push(ast); + break; + case "TypeLiteral": + if (ast.propertySignatures.length === 0 && ast.indexSignatures.length === 0) { + if (!uniques["{}"]) { + uniques["{}"] = ast; + out.push(ast); + } + } else if (!uniques["ObjectKeyword"]) out.push(ast); + break; + default: out.push(ast); + } + return out; +}; +/** +* @category model +* @since 3.10.0 +*/ +var Union$1 = class Union$1 { + types; + annotations; + static make = (types, annotations) => { + return isMembers(types) ? new Union$1(types, annotations) : types.length === 1 ? types[0] : neverKeyword; + }; + /** @internal */ + static unify = (candidates, annotations) => { + return Union$1.make(unify(flatten(candidates)), annotations); + }; + /** + * @since 3.10.0 + */ + _tag = "Union"; + constructor(types, annotations = {}) { + this.types = types; + this.annotations = annotations; + } + /** + * @since 3.10.0 + */ + toString() { + return require_Layer.getOrElse(getExpected(this), () => this.types.map(String).join(" | ")); + } + /** + * @since 3.10.0 + */ + toJSON() { + return { + _tag: this._tag, + types: this.types.map((ast) => ast.toJSON()), + annotations: toJSONAnnotations(this.annotations) + }; + } +}; +/** @internal */ +var mapMembers = (members, f) => members.map(f); +/** @internal */ +var isMembers = (as) => as.length > 1; +/** +* @category guards +* @since 3.10.0 +*/ +var isUnion = /* @__PURE__ */ createASTGuard("Union"); +var toJSONMemoMap = /* @__PURE__ */ require_Layer.globalValue(/* @__PURE__ */ Symbol.for("effect/Schema/AST/toJSONMemoMap"), () => /* @__PURE__ */ new WeakMap()); +/** +* @category model +* @since 3.10.0 +*/ +var Suspend = class { + f; + annotations; + /** + * @since 3.10.0 + */ + _tag = "Suspend"; + constructor(f, annotations = {}) { + this.f = f; + this.annotations = annotations; + this.f = memoizeThunk(f); + } + /** + * @since 3.10.0 + */ + toString() { + return getExpected(this).pipe(require_Layer.orElse(() => require_Layer.flatMap$2(require_Layer.liftThrowable(this.f)(), (ast) => getExpected(ast))), require_Layer.getOrElse(() => "")); + } + /** + * @since 3.10.0 + */ + toJSON() { + const ast = this.f(); + let out = toJSONMemoMap.get(ast); + if (out) return out; + toJSONMemoMap.set(ast, { _tag: this._tag }); + out = { + _tag: this._tag, + ast: ast.toJSON(), + annotations: toJSONAnnotations(this.annotations) + }; + toJSONMemoMap.set(ast, out); + return out; + } +}; +/** +* @category model +* @since 3.10.0 +*/ +var Refinement$1 = class { + from; + filter; + annotations; + /** + * @since 3.10.0 + */ + _tag = "Refinement"; + constructor(from, filter, annotations = {}) { + this.from = from; + this.filter = filter; + this.annotations = annotations; + } + /** + * @since 3.10.0 + */ + toString() { + return getIdentifierAnnotation(this).pipe(require_Layer.getOrElse(() => require_Layer.match(getOrElseExpected(this), { + onNone: () => `{ ${this.from} | filter }`, + onSome: (expected) => isRefinement$1(this.from) ? String(this.from) + " & " + expected : expected + }))); + } + /** + * @since 3.10.0 + */ + toJSON() { + return { + _tag: this._tag, + from: this.from.toJSON(), + annotations: toJSONAnnotations(this.annotations) + }; + } +}; +/** +* @category guards +* @since 3.10.0 +*/ +var isRefinement$1 = /* @__PURE__ */ createASTGuard("Refinement"); +/** +* @since 3.10.0 +*/ +var defaultParseOption = {}; +/** +* @category model +* @since 3.10.0 +*/ +var Transformation$1 = class { + from; + to; + transformation; + annotations; + /** + * @since 3.10.0 + */ + _tag = "Transformation"; + constructor(from, to, transformation, annotations = {}) { + this.from = from; + this.to = to; + this.transformation = transformation; + this.annotations = annotations; + } + /** + * @since 3.10.0 + */ + toString() { + return require_Layer.getOrElse(getExpected(this), () => `(${String(this.from)} <-> ${String(this.to)})`); + } + /** + * @since 3.10.0 + */ + toJSON() { + return { + _tag: this._tag, + from: this.from.toJSON(), + to: this.to.toJSON(), + annotations: toJSONAnnotations(this.annotations) + }; + } +}; +/** +* @category model +* @since 3.10.0 +*/ +var FinalTransformation = class { + decode; + encode; + /** + * @since 3.10.0 + */ + _tag = "FinalTransformation"; + constructor(decode, encode) { + this.decode = decode; + this.encode = encode; + } +}; +/** +* Represents a `PropertySignature -> PropertySignature` transformation +* +* The semantic of `decode` is: +* - `none()` represents the absence of the key/value pair +* - `some(value)` represents the presence of the key/value pair +* +* The semantic of `encode` is: +* - `none()` you don't want to output the key/value pair +* - `some(value)` you want to output the key/value pair +* +* @category model +* @since 3.10.0 +*/ +var PropertySignatureTransformation$1 = class { + from; + to; + decode; + encode; + constructor(from, to, decode, encode) { + this.from = from; + this.to = to; + this.decode = decode; + this.encode = encode; + } +}; +/** +* @category model +* @since 3.10.0 +*/ +var TypeLiteralTransformation = class { + propertySignatureTransformations; + /** + * @since 3.10.0 + */ + _tag = "TypeLiteralTransformation"; + constructor(propertySignatureTransformations) { + this.propertySignatureTransformations = propertySignatureTransformations; + const fromKeys = {}; + const toKeys = {}; + for (const pst of propertySignatureTransformations) { + const from = pst.from; + if (fromKeys[from]) throw new Error(getASTDuplicatePropertySignatureTransformationErrorMessage(from)); + fromKeys[from] = true; + const to = pst.to; + if (toKeys[to]) throw new Error(getASTDuplicatePropertySignatureTransformationErrorMessage(to)); + toKeys[to] = true; + } + } +}; +/** +* Merges a set of new annotations with existing ones, potentially overwriting +* any duplicates. +* +* Any previously existing identifier annotations are deleted. +* +* @since 3.10.0 +*/ +var annotations = (ast, overrides) => { + const d = Object.getOwnPropertyDescriptors(ast); + const base = { ...ast.annotations }; + delete base[IdentifierAnnotationId]; + const value = { + ...base, + ...overrides + }; + const surrogate = getSurrogateAnnotation(ast); + if (require_Layer.isSome(surrogate)) value[SurrogateAnnotationId] = annotations(surrogate.value, overrides); + d.annotations.value = value; + return Object.create(Object.getPrototypeOf(ast), d); +}; +var STRING_KEYWORD_PATTERN = "[\\s\\S]*?"; +var NUMBER_KEYWORD_PATTERN = "[+-]?\\d*\\.?\\d+(?:[Ee][+-]?\\d+)?"; +var getTemplateLiteralSpanTypePattern = (type, capture) => { + switch (type._tag) { + case "Literal": return require_Layer.escape(String(type.literal)); + case "StringKeyword": return STRING_KEYWORD_PATTERN; + case "NumberKeyword": return NUMBER_KEYWORD_PATTERN; + case "TemplateLiteral": return getTemplateLiteralPattern(type, capture, false); + case "Union": return type.types.map((type) => getTemplateLiteralSpanTypePattern(type, capture)).join("|"); + } +}; +var handleTemplateLiteralSpanTypeParens = (type, s, capture, top) => { + if (isUnion(type)) { + if (capture && !top) return `(?:${s})`; + } else if (!capture || !top) return s; + return `(${s})`; +}; +var getTemplateLiteralPattern = (ast, capture, top) => { + let pattern = ``; + if (ast.head !== "") { + const head = require_Layer.escape(ast.head); + pattern += capture && top ? `(${head})` : head; + } + for (const span of ast.spans) { + const spanPattern = getTemplateLiteralSpanTypePattern(span.type, capture); + pattern += handleTemplateLiteralSpanTypeParens(span.type, spanPattern, capture, top); + if (span.literal !== "") { + const literal = require_Layer.escape(span.literal); + pattern += capture && top ? `(${literal})` : literal; + } + } + return pattern; +}; +/** +* Generates a regular expression from a `TemplateLiteral` AST node. +* +* @see {@link getTemplateLiteralCapturingRegExp} for a variant that captures the pattern. +* +* @since 3.10.0 +*/ +var getTemplateLiteralRegExp = (ast) => new RegExp(`^${getTemplateLiteralPattern(ast, false, true)}$`); +/** @internal */ +var record = (key, value) => { + const propertySignatures = []; + const indexSignatures = []; + const go = (key) => { + switch (key._tag) { + case "NeverKeyword": break; + case "StringKeyword": + case "SymbolKeyword": + case "TemplateLiteral": + case "Refinement": + indexSignatures.push(new IndexSignature(key, value, true)); + break; + case "Literal": + if (require_Layer.isString(key.literal) || require_Layer.isNumber(key.literal)) propertySignatures.push(new PropertySignature(key.literal, value, false, true)); + else throw new Error(getASTUnsupportedLiteralErrorMessage(key.literal)); + break; + case "Enums": + for (const [_, name] of key.enums) propertySignatures.push(new PropertySignature(name, value, false, true)); + break; + case "UniqueSymbol": + propertySignatures.push(new PropertySignature(key.symbol, value, false, true)); + break; + case "Union": + key.types.forEach(go); + break; + default: throw new Error(getASTUnsupportedKeySchemaErrorMessage(key)); + } + }; + go(key); + return { + propertySignatures, + indexSignatures + }; +}; +/** @internal */ +var pickAnnotations = (annotationIds) => (annotated) => { + let out = void 0; + for (const id of annotationIds) if (Object.prototype.hasOwnProperty.call(annotated.annotations, id)) { + if (out === void 0) out = {}; + out[id] = annotated.annotations[id]; + } + return out; +}; +var preserveTransformationAnnotations = /* @__PURE__ */ pickAnnotations([ + ExamplesAnnotationId, + DefaultAnnotationId, + JSONSchemaAnnotationId, + ArbitraryAnnotationId, + PrettyAnnotationId, + EquivalenceAnnotationId +]); +/** +* @since 3.10.0 +*/ +var typeAST = (ast) => { + switch (ast._tag) { + case "Declaration": { + const typeParameters = changeMap(ast.typeParameters, typeAST); + return typeParameters === ast.typeParameters ? ast : new Declaration(typeParameters, ast.decodeUnknown, ast.encodeUnknown, ast.annotations); + } + case "TupleType": { + const elements = changeMap(ast.elements, (e) => { + const type = typeAST(e.type); + return type === e.type ? e : new OptionalType(type, e.isOptional); + }); + const restASTs = getRestASTs(ast.rest); + const rest = changeMap(restASTs, typeAST); + return elements === ast.elements && rest === restASTs ? ast : new TupleType(elements, rest.map((type) => new Type$1(type)), ast.isReadonly, ast.annotations); + } + case "TypeLiteral": { + const propertySignatures = changeMap(ast.propertySignatures, (p) => { + const type = typeAST(p.type); + return type === p.type ? p : new PropertySignature(p.name, type, p.isOptional, p.isReadonly); + }); + const indexSignatures = changeMap(ast.indexSignatures, (is) => { + const type = typeAST(is.type); + return type === is.type ? is : new IndexSignature(is.parameter, type, is.isReadonly); + }); + return propertySignatures === ast.propertySignatures && indexSignatures === ast.indexSignatures ? ast : new TypeLiteral(propertySignatures, indexSignatures, ast.annotations); + } + case "Union": { + const types = changeMap(ast.types, typeAST); + return types === ast.types ? ast : Union$1.make(types, ast.annotations); + } + case "Suspend": return new Suspend(() => typeAST(ast.f()), ast.annotations); + case "Refinement": { + const from = typeAST(ast.from); + return from === ast.from ? ast : new Refinement$1(from, ast.filter, ast.annotations); + } + case "Transformation": { + const preserve = preserveTransformationAnnotations(ast); + return typeAST(preserve !== void 0 ? annotations(ast.to, preserve) : ast.to); + } + } + return ast; +}; +function changeMap(as, f) { + let changed = false; + const out = require_Layer.allocate(as.length); + for (let i = 0; i < as.length; i++) { + const a = as[i]; + const fa = f(a); + if (fa !== a) changed = true; + out[i] = fa; + } + return changed ? out : as; +} +/** +* Returns the from part of a transformation if it exists +* +* @internal +*/ +var getTransformationFrom = (ast) => { + switch (ast._tag) { + case "Transformation": return ast.from; + case "Refinement": return getTransformationFrom(ast.from); + case "Suspend": return getTransformationFrom(ast.f()); + } +}; +var encodedAST_ = (ast, isBound) => { + switch (ast._tag) { + case "Declaration": { + const typeParameters = changeMap(ast.typeParameters, (ast) => encodedAST_(ast, isBound)); + return typeParameters === ast.typeParameters ? ast : new Declaration(typeParameters, ast.decodeUnknown, ast.encodeUnknown); + } + case "TupleType": { + const elements = changeMap(ast.elements, (e) => { + const type = encodedAST_(e.type, isBound); + return type === e.type ? e : new OptionalType(type, e.isOptional); + }); + const restASTs = getRestASTs(ast.rest); + const rest = changeMap(restASTs, (ast) => encodedAST_(ast, isBound)); + return elements === ast.elements && rest === restASTs ? ast : new TupleType(elements, rest.map((ast) => new Type$1(ast)), ast.isReadonly); + } + case "TypeLiteral": { + const propertySignatures = changeMap(ast.propertySignatures, (ps) => { + const type = encodedAST_(ps.type, isBound); + return type === ps.type ? ps : new PropertySignature(ps.name, type, ps.isOptional, ps.isReadonly); + }); + const indexSignatures = changeMap(ast.indexSignatures, (is) => { + const type = encodedAST_(is.type, isBound); + return type === is.type ? is : new IndexSignature(is.parameter, type, is.isReadonly); + }); + return propertySignatures === ast.propertySignatures && indexSignatures === ast.indexSignatures ? ast : new TypeLiteral(propertySignatures, indexSignatures); + } + case "Union": { + const types = changeMap(ast.types, (ast) => encodedAST_(ast, isBound)); + return types === ast.types ? ast : Union$1.make(types); + } + case "Suspend": { + let borrowedAnnotations = void 0; + const identifier = getJSONIdentifier(ast); + if (require_Layer.isSome(identifier)) { + const suffix = isBound ? "Bound" : ""; + borrowedAnnotations = { [JSONIdentifierAnnotationId]: `${identifier.value}Encoded${suffix}` }; + } + return new Suspend(() => encodedAST_(ast.f(), isBound), borrowedAnnotations); + } + case "Refinement": { + const from = encodedAST_(ast.from, isBound); + if (isBound) { + if (from === ast.from) return ast; + if (getTransformationFrom(ast.from) === void 0 && hasStableFilter(ast)) return new Refinement$1(from, ast.filter, ast.annotations); + return from; + } else return from; + } + case "Transformation": return encodedAST_(ast.from, isBound); + } + return ast; +}; +/** +* @since 3.10.0 +*/ +var encodedAST = (ast) => encodedAST_(ast, false); +var toJSONAnnotations = (annotations) => { + const out = {}; + for (const k of Object.getOwnPropertySymbols(annotations)) out[String(k)] = annotations[k]; + return out; +}; +/** @internal */ +var getEncodedParameter = (ast) => { + switch (ast._tag) { + case "StringKeyword": + case "SymbolKeyword": + case "TemplateLiteral": return ast; + case "Refinement": return getEncodedParameter(ast.from); + } +}; +var formatKeyword = (ast) => require_Layer.getOrElse(getExpected(ast), () => ast._tag); +function getBrands(ast) { + return require_Layer.match(getBrandAnnotation(ast), { + onNone: () => "", + onSome: (brands) => brands.map((brand) => ` & Brand<${require_Layer.formatUnknown(brand)}>`).join("") + }); +} +var getOrElseExpected = (ast) => getTitleAnnotation(ast).pipe(require_Layer.orElse(() => getDescriptionAnnotation(ast)), require_Layer.orElse(() => getAutoTitleAnnotation(ast)), require_Layer.map$1((s) => s + getBrands(ast))); +var getExpected = (ast) => require_Layer.orElse(getIdentifierAnnotation(ast), () => getOrElseExpected(ast)); +//#endregion +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/BigDecimal.js +/** +* This module provides utility functions and type class instances for working with the `BigDecimal` type in TypeScript. +* It includes functions for basic arithmetic operations, as well as type class instances for `Equivalence` and `Order`. +* +* A `BigDecimal` allows storing any real number to arbitrary precision; which avoids common floating point errors +* (such as 0.1 + 0.2 ≠ 0.3) at the cost of complexity. +* +* Internally, `BigDecimal` uses a `BigInt` object, paired with a 64-bit integer which determines the position of the +* decimal point. Therefore, the precision *is not* actually arbitrary, but limited to 263 decimal places. +* +* It is not recommended to convert a floating point number to a decimal directly, as the floating point representation +* may be unexpected. +* +* @module BigDecimal +* @since 2.0.0 +* @see {@link module:BigInt} for more similar operations on `bigint` types +* @see {@link module:Number} for more similar operations on `number` types +*/ +var FINITE_INT_REGEX = /^[+-]?\d+$/; +/** +* @since 2.0.0 +* @category symbols +*/ +var TypeId$2 = /* @__PURE__ */ Symbol.for("effect/BigDecimal"); +var BigDecimalProto = { + [TypeId$2]: TypeId$2, + [require_Layer.symbol$1]() { + const normalized = normalize(this); + return require_Layer.pipe(require_Layer.hash(normalized.value), require_Layer.combine(require_Layer.number(normalized.scale)), require_Layer.cached(this)); + }, + [require_Layer.symbol](that) { + return isBigDecimal(that) && equals(this, that); + }, + toString() { + return `BigDecimal(${format$1(this)})`; + }, + toJSON() { + return { + _id: "BigDecimal", + value: String(this.value), + scale: this.scale + }; + }, + [require_Layer.NodeInspectSymbol]() { + return this.toJSON(); + }, + pipe() { + return require_Layer.pipeArguments(this, arguments); + } +}; +/** +* Checks if a given value is a `BigDecimal`. +* +* @since 2.0.0 +* @category guards +*/ +var isBigDecimal = (u) => require_Layer.hasProperty(u, TypeId$2); +/** +* Creates a `BigDecimal` from a `bigint` value and a scale. +* +* @since 2.0.0 +* @category constructors +*/ +var make$1 = (value, scale) => { + const o = Object.create(BigDecimalProto); + o.value = value; + o.scale = scale; + return o; +}; +/** +* Internal function used to create pre-normalized `BigDecimal`s. +* +* @internal +*/ +var unsafeMakeNormalized = (value, scale) => { + if (value !== bigint0 && value % bigint10 === bigint0) throw new RangeError("Value must be normalized"); + const o = make$1(value, scale); + o.normalized = o; + return o; +}; +var bigint0 = /* @__PURE__ */ BigInt(0); +var bigint10 = /* @__PURE__ */ BigInt(10); +var zero = /* @__PURE__ */ unsafeMakeNormalized(bigint0, 0); +/** +* Normalizes a given `BigDecimal` by removing trailing zeros. +* +* **Example** +* +* ```ts +* import * as assert from "node:assert" +* import { normalize, make, unsafeFromString } from "effect/BigDecimal" +* +* assert.deepStrictEqual(normalize(unsafeFromString("123.00000")), normalize(make(123n, 0))) +* assert.deepStrictEqual(normalize(unsafeFromString("12300000")), normalize(make(123n, -5))) +* ``` +* +* @since 2.0.0 +* @category scaling +*/ +var normalize = (self) => { + if (self.normalized === void 0) if (self.value === bigint0) self.normalized = zero; + else { + const digits = `${self.value}`; + let trail = 0; + for (let i = digits.length - 1; i >= 0; i--) if (digits[i] === "0") trail++; + else break; + if (trail === 0) self.normalized = self; + self.normalized = unsafeMakeNormalized(BigInt(digits.substring(0, digits.length - trail)), self.scale - trail); + } + return self.normalized; +}; +/** +* Scales a given `BigDecimal` to the specified scale. +* +* If the given scale is smaller than the current scale, the value will be rounded down to +* the nearest integer. +* +* @since 2.0.0 +* @category scaling +*/ +var scale = /* @__PURE__ */ require_Layer.dual(2, (self, scale) => { + if (scale > self.scale) return make$1(self.value * bigint10 ** BigInt(scale - self.scale), scale); + if (scale < self.scale) return make$1(self.value / bigint10 ** BigInt(self.scale - scale), scale); + return self; +}); +/** +* Determines the absolute value of a given `BigDecimal`. +* +* @example +* ```ts +* import * as assert from "node:assert" +* import { abs, unsafeFromString } from "effect/BigDecimal" +* +* assert.deepStrictEqual(abs(unsafeFromString("-5")), unsafeFromString("5")) +* assert.deepStrictEqual(abs(unsafeFromString("0")), unsafeFromString("0")) +* assert.deepStrictEqual(abs(unsafeFromString("5")), unsafeFromString("5")) +* ``` +* +* @since 2.0.0 +* @category math +*/ +var abs = (n) => n.value < bigint0 ? make$1(-n.value, n.scale) : n; +/** +* @category instances +* @since 2.0.0 +*/ +var Equivalence$2 = /* @__PURE__ */ require_Layer.make((self, that) => { + if (self.scale > that.scale) return scale(that, self.scale).value === self.value; + if (self.scale < that.scale) return scale(self, that.scale).value === that.value; + return self.value === that.value; +}); +/** +* Checks if two `BigDecimal`s are equal. +* +* @since 2.0.0 +* @category predicates +*/ +var equals = /* @__PURE__ */ require_Layer.dual(2, (self, that) => Equivalence$2(self, that)); +/** +* Creates a `BigDecimal` from a `number` value. +* +* It is not recommended to convert a floating point number to a decimal directly, +* as the floating point representation may be unexpected. +* +* Throws a `RangeError` if the number is not finite (`NaN`, `+Infinity` or `-Infinity`). +* +* @example +* ```ts +* import * as assert from "node:assert" +* import { unsafeFromNumber, make } from "effect/BigDecimal" +* +* assert.deepStrictEqual(unsafeFromNumber(123), make(123n, 0)) +* assert.deepStrictEqual(unsafeFromNumber(123.456), make(123456n, 3)) +* ``` +* +* @since 3.11.0 +* @category constructors +*/ +var unsafeFromNumber = (n) => require_Layer.getOrThrowWith(safeFromNumber(n), () => /* @__PURE__ */ new RangeError(`Number must be finite, got ${n}`)); +/** +* Creates a `BigDecimal` from a `number` value. +* +* It is not recommended to convert a floating point number to a decimal directly, +* as the floating point representation may be unexpected. +* +* Returns `None` if the number is not finite (`NaN`, `+Infinity` or `-Infinity`). +* +* @example +* ```ts +* import * as assert from "node:assert" +* import { BigDecimal, Option } from "effect" +* +* assert.deepStrictEqual(BigDecimal.safeFromNumber(123), Option.some(BigDecimal.make(123n, 0))) +* assert.deepStrictEqual(BigDecimal.safeFromNumber(123.456), Option.some(BigDecimal.make(123456n, 3))) +* assert.deepStrictEqual(BigDecimal.safeFromNumber(Infinity), Option.none()) +* ``` +* +* @since 3.11.0 +* @category constructors +*/ +var safeFromNumber = (n) => { + if (!Number.isFinite(n)) return require_Layer.none$1(); + const string = `${n}`; + if (string.includes("e")) return fromString$1(string); + const [lead, trail = ""] = string.split("."); + return require_Layer.some(make$1(BigInt(`${lead}${trail}`), trail.length)); +}; +/** +* Parses a numerical `string` into a `BigDecimal`. +* +* @example +* ```ts +* import * as assert from "node:assert" +* import { BigDecimal, Option } from "effect" +* +* assert.deepStrictEqual(BigDecimal.fromString("123"), Option.some(BigDecimal.make(123n, 0))) +* assert.deepStrictEqual(BigDecimal.fromString("123.456"), Option.some(BigDecimal.make(123456n, 3))) +* assert.deepStrictEqual(BigDecimal.fromString("123.abc"), Option.none()) +* ``` +* +* @since 2.0.0 +* @category constructors +*/ +var fromString$1 = (s) => { + if (s === "") return require_Layer.some(zero); + let base; + let exp; + const seperator = s.search(/[eE]/); + if (seperator !== -1) { + const trail = s.slice(seperator + 1); + base = s.slice(0, seperator); + exp = Number(trail); + if (base === "" || !Number.isSafeInteger(exp) || !FINITE_INT_REGEX.test(trail)) return require_Layer.none$1(); + } else { + base = s; + exp = 0; + } + let digits; + let offset; + const dot = base.search(/\./); + if (dot !== -1) { + const lead = base.slice(0, dot); + const trail = base.slice(dot + 1); + digits = `${lead}${trail}`; + offset = trail.length; + } else { + digits = base; + offset = 0; + } + if (!FINITE_INT_REGEX.test(digits)) return require_Layer.none$1(); + const scale = offset - exp; + if (!Number.isSafeInteger(scale)) return require_Layer.none$1(); + return require_Layer.some(make$1(BigInt(digits), scale)); +}; +/** +* Formats a given `BigDecimal` as a `string`. +* +* If the scale of the `BigDecimal` is greater than or equal to 16, the `BigDecimal` will +* be formatted in scientific notation. +* +* @example +* ```ts +* import * as assert from "node:assert" +* import { format, unsafeFromString } from "effect/BigDecimal" +* +* assert.deepStrictEqual(format(unsafeFromString("-5")), "-5") +* assert.deepStrictEqual(format(unsafeFromString("123.456")), "123.456") +* assert.deepStrictEqual(format(unsafeFromString("-0.00000123")), "-0.00000123") +* ``` +* +* @since 2.0.0 +* @category conversions +*/ +var format$1 = (n) => { + const normalized = normalize(n); + if (Math.abs(normalized.scale) >= 16) return toExponential(normalized); + const negative = normalized.value < bigint0; + const absolute = negative ? `${normalized.value}`.substring(1) : `${normalized.value}`; + let before; + let after; + if (normalized.scale >= absolute.length) { + before = "0"; + after = "0".repeat(normalized.scale - absolute.length) + absolute; + } else { + const location = absolute.length - normalized.scale; + if (location > absolute.length) { + const zeros = location - absolute.length; + before = `${absolute}${"0".repeat(zeros)}`; + after = ""; + } else { + after = absolute.slice(location); + before = absolute.slice(0, location); + } + } + const complete = after === "" ? before : `${before}.${after}`; + return negative ? `-${complete}` : complete; +}; +/** +* Formats a given `BigDecimal` as a `string` in scientific notation. +* +* @example +* ```ts +* import * as assert from "node:assert" +* import { toExponential, make } from "effect/BigDecimal" +* +* assert.deepStrictEqual(toExponential(make(123456n, -5)), "1.23456e+10") +* ``` +* +* @since 3.11.0 +* @category conversions +*/ +var toExponential = (n) => { + if (isZero(n)) return "0e+0"; + const normalized = normalize(n); + const digits = `${abs(normalized).value}`; + const head = digits.slice(0, 1); + const tail = digits.slice(1); + let output = `${isNegative(normalized) ? "-" : ""}${head}`; + if (tail !== "") output += `.${tail}`; + const exp = tail.length - normalized.scale; + return `${output}e${exp >= 0 ? "+" : ""}${exp}`; +}; +/** +* Converts a `BigDecimal` to a `number`. +* +* This function will produce incorrect results if the `BigDecimal` exceeds the 64-bit range of a `number`. +* +* @example +* ```ts +* import * as assert from "node:assert" +* import { unsafeToNumber, unsafeFromString } from "effect/BigDecimal" +* +* assert.deepStrictEqual(unsafeToNumber(unsafeFromString("123.456")), 123.456) +* ``` +* +* @since 2.0.0 +* @category conversions +*/ +var unsafeToNumber = (n) => Number(format$1(n)); +/** +* Checks if a given `BigDecimal` is `0`. +* +* @example +* ```ts +* import * as assert from "node:assert" +* import { isZero, unsafeFromString } from "effect/BigDecimal" +* +* assert.deepStrictEqual(isZero(unsafeFromString("0")), true) +* assert.deepStrictEqual(isZero(unsafeFromString("1")), false) +* ``` +* +* @since 2.0.0 +* @category predicates +*/ +var isZero = (n) => n.value === bigint0; +/** +* Checks if a given `BigDecimal` is negative. +* +* @example +* ```ts +* import * as assert from "node:assert" +* import { isNegative, unsafeFromString } from "effect/BigDecimal" +* +* assert.deepStrictEqual(isNegative(unsafeFromString("-1")), true) +* assert.deepStrictEqual(isNegative(unsafeFromString("0")), false) +* assert.deepStrictEqual(isNegative(unsafeFromString("1")), false) +* ``` +* +* @since 2.0.0 +* @category predicates +*/ +var isNegative = (n) => n.value < bigint0; +//#endregion +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/BigInt.js +/** +* Takes a `bigint` and returns an `Option` of `number`. +* +* If the `bigint` is outside the safe integer range for JavaScript (`Number.MAX_SAFE_INTEGER` +* and `Number.MIN_SAFE_INTEGER`), it returns `Option.none()`. Otherwise, it converts the `bigint` +* to a number and returns `Option.some(number)`. +* +* @example +* ```ts +* import * as assert from "node:assert" +* import { BigInt as BI, Option } from "effect" +* +* assert.deepStrictEqual(BI.toNumber(BigInt(42)), Option.some(42)) +* assert.deepStrictEqual(BI.toNumber(BigInt(Number.MAX_SAFE_INTEGER) + BigInt(1)), Option.none()) +* assert.deepStrictEqual(BI.toNumber(BigInt(Number.MIN_SAFE_INTEGER) - BigInt(1)), Option.none()) +* ``` +* +* @category conversions +* @since 2.0.0 +*/ +var toNumber = (b) => { + if (b > BigInt(Number.MAX_SAFE_INTEGER) || b < BigInt(Number.MIN_SAFE_INTEGER)) return require_Layer.none$1(); + return require_Layer.some(Number(b)); +}; +/** +* Takes a string and returns an `Option` of `bigint`. +* +* If the string is empty or contains characters that cannot be converted into a `bigint`, +* it returns `Option.none()`, otherwise, it returns `Option.some(bigint)`. +* +* @example +* ```ts +* import * as assert from "node:assert" +* import { BigInt as BI, Option } from "effect" +* +* assert.deepStrictEqual(BI.fromString("42"), Option.some(BigInt(42))) +* assert.deepStrictEqual(BI.fromString(" "), Option.none()) +* assert.deepStrictEqual(BI.fromString("a"), Option.none()) +* ``` +* +* @category conversions +* @since 2.4.12 +*/ +var fromString = (s) => { + try { + return s.trim() === "" ? require_Layer.none$1() : require_Layer.some(BigInt(s)); + } catch { + return require_Layer.none$1(); + } +}; +/** +* Takes a number and returns an `Option` of `bigint`. +* +* If the number is outside the safe integer range for JavaScript (`Number.MAX_SAFE_INTEGER` +* and `Number.MIN_SAFE_INTEGER`), it returns `Option.none()`. Otherwise, it attempts to +* convert the number to a `bigint` and returns `Option.some(bigint)`. +* +* @example +* ```ts +* import * as assert from "node:assert" +* import { BigInt as BI, Option } from "effect" +* +* assert.deepStrictEqual(BI.fromNumber(42), Option.some(BigInt(42))) +* assert.deepStrictEqual(BI.fromNumber(Number.MAX_SAFE_INTEGER + 1), Option.none()) +* assert.deepStrictEqual(BI.fromNumber(Number.MIN_SAFE_INTEGER - 1), Option.none()) +* ``` +* +* @category conversions +* @since 2.4.12 +*/ +var fromNumber = (n) => { + if (n > Number.MAX_SAFE_INTEGER || n < Number.MIN_SAFE_INTEGER) return require_Layer.none$1(); + try { + return require_Layer.some(BigInt(n)); + } catch { + return require_Layer.none$1(); + } +}; +//#endregion +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Data.js +/** +* Provides a constructor for a Case Class. +* +* @since 2.0.0 +* @category constructors +*/ +var Error$1 = /* @__PURE__ */ function() { + const plainArgsSymbol = /* @__PURE__ */ Symbol.for("effect/Data/Error/plainArgs"); + return { BaseEffectError: class extends require_Layer.YieldableError { + constructor(args) { + super(args?.message, args?.cause ? { cause: args.cause } : void 0); + if (args) { + Object.assign(this, args); + Object.defineProperty(this, plainArgsSymbol, { + value: args, + enumerable: false + }); + } + } + toJSON() { + return { + ...this[plainArgsSymbol], + ...this + }; + } + } }.BaseEffectError; +}(); +/** +* @since 2.0.0 +* @category constructors +*/ +var TaggedError = (tag) => { + const O = { BaseEffectError: class extends Error$1 { + _tag = tag; + } }; + O.BaseEffectError.prototype.name = tag; + return O.BaseEffectError; +}; +//#endregion +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/dateTime.js +/** @internal */ +var TypeId$1 = /* @__PURE__ */ Symbol.for("effect/DateTime"); +/** @internal */ +var TimeZoneTypeId = /* @__PURE__ */ Symbol.for("effect/DateTime/TimeZone"); +var Proto = { + [TypeId$1]: TypeId$1, + pipe() { + return require_Layer.pipeArguments(this, arguments); + }, + [require_Layer.NodeInspectSymbol]() { + return this.toString(); + }, + toJSON() { + return toDateUtc$1(this).toJSON(); + } +}; +var ProtoUtc = { + ...Proto, + _tag: "Utc", + [require_Layer.symbol$1]() { + return require_Layer.cached(this, require_Layer.number(this.epochMillis)); + }, + [require_Layer.symbol](that) { + return isDateTime$1(that) && that._tag === "Utc" && this.epochMillis === that.epochMillis; + }, + toString() { + return `DateTime.Utc(${toDateUtc$1(this).toJSON()})`; + } +}; +var ProtoZoned = { + ...Proto, + _tag: "Zoned", + [require_Layer.symbol$1]() { + return require_Layer.pipe(require_Layer.number(this.epochMillis), require_Layer.combine(require_Layer.hash(this.zone)), require_Layer.cached(this)); + }, + [require_Layer.symbol](that) { + return isDateTime$1(that) && that._tag === "Zoned" && this.epochMillis === that.epochMillis && require_Layer.equals(this.zone, that.zone); + }, + toString() { + return `DateTime.Zoned(${formatIsoZoned$1(this)})`; + } +}; +var ProtoTimeZone = { + [TimeZoneTypeId]: TimeZoneTypeId, + [require_Layer.NodeInspectSymbol]() { + return this.toString(); + } +}; +var ProtoTimeZoneNamed = { + ...ProtoTimeZone, + _tag: "Named", + [require_Layer.symbol$1]() { + return require_Layer.cached(this, require_Layer.string(`Named:${this.id}`)); + }, + [require_Layer.symbol](that) { + return isTimeZone(that) && that._tag === "Named" && this.id === that.id; + }, + toString() { + return `TimeZone.Named(${this.id})`; + }, + toJSON() { + return { + _id: "TimeZone", + _tag: "Named", + id: this.id + }; + } +}; +var ProtoTimeZoneOffset = { + ...ProtoTimeZone, + _tag: "Offset", + [require_Layer.symbol$1]() { + return require_Layer.cached(this, require_Layer.string(`Offset:${this.offset}`)); + }, + [require_Layer.symbol](that) { + return isTimeZone(that) && that._tag === "Offset" && this.offset === that.offset; + }, + toString() { + return `TimeZone.Offset(${offsetToString(this.offset)})`; + }, + toJSON() { + return { + _id: "TimeZone", + _tag: "Offset", + offset: this.offset + }; + } +}; +/** @internal */ +var makeZonedProto = (epochMillis, zone, partsUtc) => { + const self = Object.create(ProtoZoned); + self.epochMillis = epochMillis; + self.zone = zone; + Object.defineProperty(self, "partsUtc", { + value: partsUtc, + enumerable: false, + writable: true + }); + Object.defineProperty(self, "adjustedEpochMillis", { + value: void 0, + enumerable: false, + writable: true + }); + Object.defineProperty(self, "partsAdjusted", { + value: void 0, + enumerable: false, + writable: true + }); + return self; +}; +/** @internal */ +var isDateTime$1 = (u) => require_Layer.hasProperty(u, TypeId$1); +/** @internal */ +var isTimeZone = (u) => require_Layer.hasProperty(u, TimeZoneTypeId); +/** @internal */ +var isTimeZoneOffset$1 = (u) => isTimeZone(u) && u._tag === "Offset"; +/** @internal */ +var isTimeZoneNamed$1 = (u) => isTimeZone(u) && u._tag === "Named"; +/** @internal */ +var isUtc$1 = (self) => self._tag === "Utc"; +/** @internal */ +var isZoned$1 = (self) => self._tag === "Zoned"; +/** @internal */ +var Equivalence$1 = /* @__PURE__ */ require_Layer.make((a, b) => a.epochMillis === b.epochMillis); +var makeUtc = (epochMillis) => { + const self = Object.create(ProtoUtc); + self.epochMillis = epochMillis; + Object.defineProperty(self, "partsUtc", { + value: void 0, + enumerable: false, + writable: true + }); + return self; +}; +/** @internal */ +var unsafeFromDate$1 = (date) => { + const epochMillis = date.getTime(); + if (Number.isNaN(epochMillis)) throw new require_Layer.IllegalArgumentException("Invalid date"); + return makeUtc(epochMillis); +}; +/** @internal */ +var unsafeMake$1 = (input) => { + if (isDateTime$1(input)) return input; + else if (input instanceof Date) return unsafeFromDate$1(input); + else if (typeof input === "object") { + const date = /* @__PURE__ */ new Date(0); + setPartsDate(date, input); + return unsafeFromDate$1(date); + } else if (typeof input === "string" && !hasZone(input)) return unsafeFromDate$1(/* @__PURE__ */ new Date(input + "Z")); + return unsafeFromDate$1(new Date(input)); +}; +var hasZone = (input) => /Z|[+-]\d{2}$|[+-]\d{2}:?\d{2}$|\]$/.test(input); +var minEpochMillis = -86399999568e5; +var maxEpochMillis = 864e13 - 840 * 60 * 1e3; +/** @internal */ +var unsafeMakeZoned$1 = (input, options) => { + if (options?.timeZone === void 0 && isDateTime$1(input) && isZoned$1(input)) return input; + const self = unsafeMake$1(input); + if (self.epochMillis < minEpochMillis || self.epochMillis > maxEpochMillis) throw new RangeError(`Epoch millis out of range: ${self.epochMillis}`); + let zone; + if (options?.timeZone === void 0) zone = zoneMakeOffset$1(new Date(self.epochMillis).getTimezoneOffset() * -60 * 1e3); + else if (isTimeZone(options?.timeZone)) zone = options.timeZone; + else if (typeof options?.timeZone === "number") zone = zoneMakeOffset$1(options.timeZone); + else { + const parsedZone = zoneFromString$1(options.timeZone); + if (require_Layer.isNone(parsedZone)) throw new require_Layer.IllegalArgumentException(`Invalid time zone: ${options.timeZone}`); + zone = parsedZone.value; + } + if (options?.adjustForTimeZone !== true) return makeZonedProto(self.epochMillis, zone, self.partsUtc); + return makeZonedFromAdjusted(self.epochMillis, zone, options?.disambiguation ?? "compatible"); +}; +/** @internal */ +var makeZoned = /* @__PURE__ */ require_Layer.liftThrowable(unsafeMakeZoned$1); +var zonedStringRegex = /^(.{17,35})\[(.+)\]$/; +/** @internal */ +var makeZonedFromString$1 = (input) => { + const match = zonedStringRegex.exec(input); + if (match === null) { + const offset = parseOffset(input); + return offset !== null ? makeZoned(input, { timeZone: offset }) : require_Layer.none$1(); + } + const [, isoString, timeZone] = match; + return makeZoned(isoString, { timeZone }); +}; +var validZoneCache = /* @__PURE__ */ require_Layer.globalValue("effect/DateTime/validZoneCache", () => /* @__PURE__ */ new Map()); +var formatOptions = { + day: "numeric", + month: "numeric", + year: "numeric", + hour: "numeric", + minute: "numeric", + second: "numeric", + timeZoneName: "longOffset", + fractionalSecondDigits: 3, + hourCycle: "h23" +}; +var zoneMakeIntl = (format) => { + const zoneId = format.resolvedOptions().timeZone; + if (validZoneCache.has(zoneId)) return validZoneCache.get(zoneId); + const zone = Object.create(ProtoTimeZoneNamed); + zone.id = zoneId; + zone.format = format; + validZoneCache.set(zoneId, zone); + return zone; +}; +/** @internal */ +var zoneUnsafeMakeNamed$1 = (zoneId) => { + if (validZoneCache.has(zoneId)) return validZoneCache.get(zoneId); + try { + return zoneMakeIntl(new Intl.DateTimeFormat("en-US", { + ...formatOptions, + timeZone: zoneId + })); + } catch { + throw new require_Layer.IllegalArgumentException(`Invalid time zone: ${zoneId}`); + } +}; +/** @internal */ +var zoneMakeOffset$1 = (offset) => { + const zone = Object.create(ProtoTimeZoneOffset); + zone.offset = offset; + return zone; +}; +/** @internal */ +var zoneMakeNamed = /* @__PURE__ */ require_Layer.liftThrowable(zoneUnsafeMakeNamed$1); +var offsetZoneRegex = /^(?:GMT|[+-])/; +/** @internal */ +var zoneFromString$1 = (zone) => { + if (offsetZoneRegex.test(zone)) { + const offset = parseOffset(zone); + return offset === null ? require_Layer.none$1() : require_Layer.some(zoneMakeOffset$1(offset)); + } + return zoneMakeNamed(zone); +}; +/** @internal */ +var zoneToString$1 = (self) => { + if (self._tag === "Offset") return offsetToString(self.offset); + return self.id; +}; +/** @internal */ +var toDateUtc$1 = (self) => new Date(self.epochMillis); +/** @internal */ +var toDate = (self) => { + if (self._tag === "Utc") return new Date(self.epochMillis); + else if (self.zone._tag === "Offset") return new Date(self.epochMillis + self.zone.offset); + else if (self.adjustedEpochMillis !== void 0) return new Date(self.adjustedEpochMillis); + const parts = self.zone.format.formatToParts(self.epochMillis).filter((_) => _.type !== "literal"); + const date = /* @__PURE__ */ new Date(0); + date.setUTCFullYear(Number(parts[2].value), Number(parts[0].value) - 1, Number(parts[1].value)); + date.setUTCHours(Number(parts[3].value), Number(parts[4].value), Number(parts[5].value), Number(parts[6].value)); + self.adjustedEpochMillis = date.getTime(); + return date; +}; +/** @internal */ +var zonedOffset = (self) => { + return toDate(self).getTime() - toEpochMillis$1(self); +}; +var offsetToString = (offset) => { + const abs = Math.abs(offset); + let hours = Math.floor(abs / (3600 * 1e3)); + let minutes = Math.round(abs % (3600 * 1e3) / (60 * 1e3)); + if (minutes === 60) { + hours += 1; + minutes = 0; + } + return `${offset < 0 ? "-" : "+"}${String(hours).padStart(2, "0")}:${String(minutes).padStart(2, "0")}`; +}; +/** @internal */ +var zonedOffsetIso = (self) => offsetToString(zonedOffset(self)); +/** @internal */ +var toEpochMillis$1 = (self) => self.epochMillis; +var setPartsDate = (date, parts) => { + if (parts.year !== void 0) date.setUTCFullYear(parts.year); + if (parts.month !== void 0) date.setUTCMonth(parts.month - 1); + if (parts.day !== void 0) date.setUTCDate(parts.day); + if (parts.weekDay !== void 0) { + const diff = parts.weekDay - date.getUTCDay(); + date.setUTCDate(date.getUTCDate() + diff); + } + if (parts.hours !== void 0) date.setUTCHours(parts.hours); + if (parts.minutes !== void 0) date.setUTCMinutes(parts.minutes); + if (parts.seconds !== void 0) date.setUTCSeconds(parts.seconds); + if (parts.millis !== void 0) date.setUTCMilliseconds(parts.millis); +}; +var constDayMillis = 1440 * 60 * 1e3; +var makeZonedFromAdjusted = (adjustedMillis, zone, disambiguation) => { + if (zone._tag === "Offset") return makeZonedProto(adjustedMillis - zone.offset, zone); + const beforeOffset = calculateNamedOffset(adjustedMillis - constDayMillis, adjustedMillis, zone); + const afterOffset = calculateNamedOffset(adjustedMillis + constDayMillis, adjustedMillis, zone); + if (beforeOffset === afterOffset) return makeZonedProto(adjustedMillis - beforeOffset, zone); + const isForwards = beforeOffset < afterOffset; + const transitionMillis = beforeOffset - afterOffset; + if (isForwards) { + if (calculateNamedOffset(adjustedMillis - afterOffset, adjustedMillis, zone) === afterOffset) return makeZonedProto(adjustedMillis - afterOffset, zone); + const before = makeZonedProto(adjustedMillis - beforeOffset, zone); + if (adjustedMillis !== toDate(before).getTime()) switch (disambiguation) { + case "reject": { + const formatted = new Date(adjustedMillis).toISOString(); + throw new RangeError(`Gap time: ${formatted} does not exist in time zone ${zone.id}`); + } + case "earlier": return makeZonedProto(adjustedMillis - afterOffset, zone); + case "compatible": + case "later": return before; + } + return before; + } + if (calculateNamedOffset(adjustedMillis - beforeOffset, adjustedMillis, zone) === beforeOffset) { + if (disambiguation === "earlier" || disambiguation === "compatible") return makeZonedProto(adjustedMillis - beforeOffset, zone); + if (calculateNamedOffset(adjustedMillis - beforeOffset + transitionMillis, adjustedMillis + transitionMillis, zone) === beforeOffset) return makeZonedProto(adjustedMillis - beforeOffset, zone); + if (disambiguation === "reject") { + const formatted = new Date(adjustedMillis).toISOString(); + throw new RangeError(`Ambiguous time: ${formatted} occurs twice in time zone ${zone.id}`); + } + } + return makeZonedProto(adjustedMillis - afterOffset, zone); +}; +var offsetRegex = /([+-])(\d{2}):(\d{2})$/; +var parseOffset = (offset) => { + const match = offsetRegex.exec(offset); + if (match === null) return null; + const [, sign, hours, minutes] = match; + return (sign === "+" ? 1 : -1) * (Number(hours) * 60 + Number(minutes)) * 60 * 1e3; +}; +var calculateNamedOffset = (utcMillis, adjustedMillis, zone) => { + const offset = zone.format.formatToParts(utcMillis).find((_) => _.type === "timeZoneName")?.value ?? ""; + if (offset === "GMT") return 0; + const result = parseOffset(offset); + if (result === null) return zonedOffset(makeZonedProto(adjustedMillis, zone)); + return result; +}; +/** @internal */ +var formatIso$1 = (self) => toDateUtc$1(self).toISOString(); +/** @internal */ +var formatIsoOffset = (self) => { + const date = toDate(self); + return self._tag === "Utc" ? date.toISOString() : `${date.toISOString().slice(0, -1)}${zonedOffsetIso(self)}`; +}; +/** @internal */ +var formatIsoZoned$1 = (self) => self.zone._tag === "Offset" ? formatIsoOffset(self) : `${formatIsoOffset(self)}[${self.zone.id}]`; +//#endregion +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/String.js +/** +* @example +* ```ts +* import * as assert from "node:assert" +* import { pipe, String } from "effect" +* +* assert.deepStrictEqual(pipe('a', String.toUpperCase), 'A') +* ``` +* +* @since 2.0.0 +*/ +var toUpperCase = (self) => self.toUpperCase(); +/** +* @example +* ```ts +* import * as assert from "node:assert" +* import { pipe, String } from "effect" +* +* assert.deepStrictEqual(pipe('A', String.toLowerCase), 'a') +* ``` +* +* @since 2.0.0 +*/ +var toLowerCase = (self) => self.toLowerCase(); +/** +* @example +* ```ts +* import * as assert from "node:assert" +* import { pipe, String } from "effect" +* +* assert.deepStrictEqual(pipe('abc', String.capitalize), 'Abc') +* ``` +* +* @since 2.0.0 +*/ +var capitalize = (self) => { + if (self.length === 0) return self; + return toUpperCase(self[0]) + self.slice(1); +}; +/** +* @example +* ```ts +* import * as assert from "node:assert" +* import { pipe, String } from "effect" +* +* assert.deepStrictEqual(pipe('ABC', String.uncapitalize), 'aBC') +* ``` +* +* @since 2.0.0 +*/ +var uncapitalize = (self) => { + if (self.length === 0) return self; + return toLowerCase(self[0]) + self.slice(1); +}; +/** +* Test whether a `string` is non empty. +* +* @since 2.0.0 +*/ +var isNonEmpty = (self) => self.length > 0; +//#endregion +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/DateTime.js +/** +* @since 3.6.0 +* @category guards +*/ +var isDateTime = isDateTime$1; +/** +* @since 3.6.0 +* @category guards +*/ +var isTimeZoneOffset = isTimeZoneOffset$1; +/** +* @since 3.6.0 +* @category guards +*/ +var isTimeZoneNamed = isTimeZoneNamed$1; +/** +* @since 3.6.0 +* @category guards +*/ +var isUtc = isUtc$1; +/** +* @since 3.6.0 +* @category guards +*/ +var isZoned = isZoned$1; +/** +* @since 3.6.0 +* @category instances +*/ +var Equivalence = Equivalence$1; +/** +* Create a `DateTime` from a `Date`. +* +* If the `Date` is invalid, an `IllegalArgumentException` will be thrown. +* +* @since 3.6.0 +* @category constructors +*/ +var unsafeFromDate = unsafeFromDate$1; +/** +* Create a `DateTime` from one of the following: +* +* - A `DateTime` +* - A `Date` instance (invalid dates will throw an `IllegalArgumentException`) +* - The `number` of milliseconds since the Unix epoch +* - An object with the parts of a date +* - A `string` that can be parsed by `Date.parse` +* +* @since 3.6.0 +* @category constructors +* @example +* ```ts +* import { DateTime } from "effect" +* +* // from Date +* DateTime.unsafeMake(new Date()) +* +* // from parts +* DateTime.unsafeMake({ year: 2024 }) +* +* // from string +* DateTime.unsafeMake("2024-01-01") +* ``` +*/ +var unsafeMake = unsafeMake$1; +/** +* Create a `DateTime.Zoned` using `DateTime.unsafeMake` and a time zone. +* +* The input is treated as UTC and then the time zone is attached, unless +* `adjustForTimeZone` is set to `true`. In that case, the input is treated as +* already in the time zone. +* +* When `adjustForTimeZone` is true and ambiguous times occur during DST transitions, +* the `disambiguation` option controls how to resolve the ambiguity: +* - `compatible` (default): Choose earlier time for repeated times, later for gaps +* - `earlier`: Always choose the earlier of two possible times +* - `later`: Always choose the later of two possible times +* - `reject`: Throw an error when ambiguous times are encountered +* +* @since 3.6.0 +* @category constructors +* @example +* ```ts +* import { DateTime } from "effect" +* +* DateTime.unsafeMakeZoned(new Date(), { timeZone: "Europe/London" }) +* ``` +*/ +var unsafeMakeZoned = unsafeMakeZoned$1; +/** +* Create a `DateTime.Zoned` from a string. +* +* It uses the format: `YYYY-MM-DDTHH:mm:ss.sss+HH:MM[Time/Zone]`. +* +* @since 3.6.0 +* @category constructors +*/ +var makeZonedFromString = makeZonedFromString$1; +/** +* Attempt to create a named time zone from a IANA time zone identifier. +* +* If the time zone is invalid, an `IllegalArgumentException` will be thrown. +* +* @since 3.6.0 +* @category time zones +*/ +var zoneUnsafeMakeNamed = zoneUnsafeMakeNamed$1; +/** +* Create a fixed offset time zone. +* +* @since 3.6.0 +* @category time zones +*/ +var zoneMakeOffset = zoneMakeOffset$1; +/** +* Try parse a TimeZone from a string +* +* @since 3.6.0 +* @category time zones +*/ +var zoneFromString = zoneFromString$1; +/** +* Format a `TimeZone` as a string. +* +* @since 3.6.0 +* @category time zones +* @example +* ```ts +* import { DateTime, Effect } from "effect" +* +* // Outputs "+03:00" +* DateTime.zoneToString(DateTime.zoneMakeOffset(3 * 60 * 60 * 1000)) +* +* // Outputs "Europe/London" +* DateTime.zoneToString(DateTime.zoneUnsafeMakeNamed("Europe/London")) +* ``` +*/ +var zoneToString = zoneToString$1; +/** +* Get the UTC `Date` of a `DateTime`. +* +* @since 3.6.0 +* @category conversions +*/ +var toDateUtc = toDateUtc$1; +/** +* Get the milliseconds since the Unix epoch of a `DateTime`. +* +* @since 3.6.0 +* @category conversions +*/ +var toEpochMillis = toEpochMillis$1; +require_Layer.Tag("effect/DateTime/CurrentTimeZone")(); +/** +* Format a `DateTime` as a UTC ISO string. +* +* @since 3.6.0 +* @category formatting +*/ +var formatIso = formatIso$1; +/** +* Format a `DateTime.Zoned` as a string. +* +* It uses the format: `YYYY-MM-DDTHH:mm:ss.sss+HH:MM[Time/Zone]`. +* +* @since 3.6.0 +* @category formatting +*/ +var formatIsoZoned = formatIsoZoned$1; +//#endregion +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/ParseResult.js +/** +* @since 3.10.0 +*/ +/** +* @category model +* @since 3.10.0 +*/ +var Pointer = class { + path; + actual; + issue; + /** + * @since 3.10.0 + */ + _tag = "Pointer"; + constructor(path, actual, issue) { + this.path = path; + this.actual = actual; + this.issue = issue; + } +}; +/** +* Error that occurs when an unexpected key or index is present. +* +* @category model +* @since 3.10.0 +*/ +var Unexpected = class { + actual; + message; + /** + * @since 3.10.0 + */ + _tag = "Unexpected"; + constructor(actual, message) { + this.actual = actual; + this.message = message; + } +}; +/** +* Error that occurs when a required key or index is missing. +* +* @category model +* @since 3.10.0 +*/ +var Missing = class { + ast; + message; + /** + * @since 3.10.0 + */ + _tag = "Missing"; + /** + * @since 3.10.0 + */ + actual = void 0; + constructor(ast, message) { + this.ast = ast; + this.message = message; + } +}; +/** +* Error that contains multiple issues. +* +* @category model +* @since 3.10.0 +*/ +var Composite = class { + ast; + actual; + issues; + output; + /** + * @since 3.10.0 + */ + _tag = "Composite"; + constructor(ast, actual, issues, output) { + this.ast = ast; + this.actual = actual; + this.issues = issues; + this.output = output; + } +}; +/** +* Error that occurs when a refinement has an error. +* +* @category model +* @since 3.10.0 +*/ +var Refinement = class { + ast; + actual; + kind; + issue; + /** + * @since 3.10.0 + */ + _tag = "Refinement"; + constructor(ast, actual, kind, issue) { + this.ast = ast; + this.actual = actual; + this.kind = kind; + this.issue = issue; + } +}; +/** +* Error that occurs when a transformation has an error. +* +* @category model +* @since 3.10.0 +*/ +var Transformation = class { + ast; + actual; + kind; + issue; + /** + * @since 3.10.0 + */ + _tag = "Transformation"; + constructor(ast, actual, kind, issue) { + this.ast = ast; + this.actual = actual; + this.kind = kind; + this.issue = issue; + } +}; +/** +* The `Type` variant of the `ParseIssue` type represents an error that occurs when the `actual` value is not of the expected type. +* The `ast` field specifies the expected type, and the `actual` field contains the value that caused the error. +* +* @category model +* @since 3.10.0 +*/ +var Type = class { + ast; + actual; + message; + /** + * @since 3.10.0 + */ + _tag = "Type"; + constructor(ast, actual, message) { + this.ast = ast; + this.actual = actual; + this.message = message; + } +}; +/** +* The `Forbidden` variant of the `ParseIssue` type represents a forbidden operation, such as when encountering an Effect that is not allowed to execute (e.g., using `runSync`). +* +* @category model +* @since 3.10.0 +*/ +var Forbidden = class { + ast; + actual; + message; + /** + * @since 3.10.0 + */ + _tag = "Forbidden"; + constructor(ast, actual, message) { + this.ast = ast; + this.actual = actual; + this.message = message; + } +}; +/** +* @category type id +* @since 3.10.0 +*/ +var ParseErrorTypeId = /* @__PURE__ */ Symbol.for("effect/Schema/ParseErrorTypeId"); +/** +* @since 3.10.0 +*/ +var ParseError = class extends TaggedError("ParseError") { + /** + * @since 3.10.0 + */ + [ParseErrorTypeId] = ParseErrorTypeId; + get message() { + return this.toString(); + } + /** + * @since 3.10.0 + */ + toString() { + return TreeFormatter.formatIssueSync(this.issue); + } + /** + * @since 3.10.0 + */ + toJSON() { + return { + _id: "ParseError", + message: this.toString() + }; + } + /** + * @since 3.10.0 + */ + [require_Layer.NodeInspectSymbol]() { + return this.toJSON(); + } +}; +/** +* @category constructors +* @since 3.10.0 +*/ +var parseError = (issue) => new ParseError({ issue }); +/** +* @category constructors +* @since 3.10.0 +*/ +var succeed = require_Layer.right; +/** +* @category constructors +* @since 3.10.0 +*/ +var fail = require_Layer.left; +var _try = require_Layer.try_$1; +/** +* @category constructors +* @since 3.10.0 +*/ +var fromOption = require_Layer.fromOption; +var isEither = require_Layer.isEither; +/** +* @category optimisation +* @since 3.10.0 +*/ +var flatMap = /* @__PURE__ */ require_Layer.dual(2, (self, f) => { + return isEither(self) ? require_Layer.match$1(self, { + onLeft: require_Layer.left, + onRight: f + }) : require_Layer.flatMap(self, f); +}); +/** +* @category optimisation +* @since 3.10.0 +*/ +var map = /* @__PURE__ */ require_Layer.dual(2, (self, f) => { + return isEither(self) ? require_Layer.map$2(self, f) : require_Layer.map(self, f); +}); +/** +* @category optimisation +* @since 3.10.0 +*/ +var mapError = /* @__PURE__ */ require_Layer.dual(2, (self, f) => { + return isEither(self) ? require_Layer.mapLeft(self, f) : require_Layer.mapError(self, f); +}); +/** +* @category optimisation +* @since 3.10.0 +*/ +var mapBoth = /* @__PURE__ */ require_Layer.dual(2, (self, options) => { + return isEither(self) ? require_Layer.mapBoth$1(self, { + onLeft: options.onFailure, + onRight: options.onSuccess + }) : require_Layer.mapBoth(self, options); +}); +/** +* @category optimisation +* @since 3.10.0 +*/ +var orElse = /* @__PURE__ */ require_Layer.dual(2, (self, f) => { + return isEither(self) ? require_Layer.match$1(self, { + onLeft: f, + onRight: require_Layer.right + }) : require_Layer.catchAll(self, f); +}); +/** @internal */ +var mergeInternalOptions = (options, overrideOptions) => { + if (overrideOptions === void 0 || require_Layer.isNumber(overrideOptions)) return options; + if (options === void 0) return overrideOptions; + return { + ...options, + ...overrideOptions + }; +}; +var getEither = (ast, isDecoding, options) => { + const parser = goMemo(ast, isDecoding); + return (u, overrideOptions) => parser(u, mergeInternalOptions(options, overrideOptions)); +}; +var getSync = (ast, isDecoding, options) => { + const parser = getEither(ast, isDecoding, options); + return (input, overrideOptions) => require_Layer.getOrThrowWith$1(parser(input, overrideOptions), parseError); +}; +var getEffect = (ast, isDecoding, options) => { + const parser = goMemo(ast, isDecoding); + return (input, overrideOptions) => parser(input, { + ...mergeInternalOptions(options, overrideOptions), + isEffectAllowed: true + }); +}; +/** +* @category decoding +* @since 3.10.0 +*/ +var decodeUnknownEither$1 = (schema, options) => getEither(schema.ast, true, options); +/** +* @category decoding +* @since 3.10.0 +*/ +var decodeUnknown = (schema, options) => getEffect(schema.ast, true, options); +/** +* @category encoding +* @since 3.10.0 +*/ +var encodeUnknown = (schema, options) => getEffect(schema.ast, false, options); +/** +* @throws `ParseError` +* @category validation +* @since 3.10.0 +*/ +var validateSync = (schema, options) => getSync(typeAST(schema.ast), true, options); +var decodeMemoMap = /* @__PURE__ */ require_Layer.globalValue(/* @__PURE__ */ Symbol.for("effect/ParseResult/decodeMemoMap"), () => /* @__PURE__ */ new WeakMap()); +var encodeMemoMap = /* @__PURE__ */ require_Layer.globalValue(/* @__PURE__ */ Symbol.for("effect/ParseResult/encodeMemoMap"), () => /* @__PURE__ */ new WeakMap()); +var goMemo = (ast, isDecoding) => { + const memoMap = isDecoding ? decodeMemoMap : encodeMemoMap; + const memo = memoMap.get(ast); + if (memo) return memo; + const raw = go(ast, isDecoding); + const parseOptionsAnnotation = getParseOptionsAnnotation(ast); + const parserWithOptions = require_Layer.isSome(parseOptionsAnnotation) ? (i, options) => raw(i, mergeInternalOptions(options, parseOptionsAnnotation.value)) : raw; + const decodingFallbackAnnotation = getDecodingFallbackAnnotation(ast); + const parser = isDecoding && require_Layer.isSome(decodingFallbackAnnotation) ? (i, options) => handleForbidden(orElse(parserWithOptions(i, options), decodingFallbackAnnotation.value), ast, i, options) : parserWithOptions; + memoMap.set(ast, parser); + return parser; +}; +var getConcurrency = (ast) => require_Layer.getOrUndefined(getConcurrencyAnnotation(ast)); +var getBatching = (ast) => require_Layer.getOrUndefined(getBatchingAnnotation(ast)); +var go = (ast, isDecoding) => { + switch (ast._tag) { + case "Refinement": if (isDecoding) { + const from = goMemo(ast.from, true); + return (i, options) => { + options = options ?? defaultParseOption; + const allErrors = options?.errors === "all"; + return handleForbidden(flatMap(orElse(from(i, options), (ef) => { + const issue = new Refinement(ast, i, "From", ef); + if (allErrors && hasStableFilter(ast) && isComposite(ef)) return require_Layer.match(ast.filter(i, options, ast), { + onNone: () => require_Layer.left(issue), + onSome: (ep) => require_Layer.left(new Composite(ast, i, [issue, new Refinement(ast, i, "Predicate", ep)])) + }); + return require_Layer.left(issue); + }), (a) => require_Layer.match(ast.filter(a, options, ast), { + onNone: () => require_Layer.right(a), + onSome: (ep) => require_Layer.left(new Refinement(ast, i, "Predicate", ep)) + })), ast, i, options); + }; + } else { + const from = goMemo(typeAST(ast), true); + const to = goMemo(dropRightRefinement(ast.from), false); + return (i, options) => handleForbidden(flatMap(from(i, options), (a) => to(a, options)), ast, i, options); + } + case "Transformation": { + const transform = getFinalTransformation(ast.transformation, isDecoding); + const from = isDecoding ? goMemo(ast.from, true) : goMemo(ast.to, false); + const to = isDecoding ? goMemo(ast.to, true) : goMemo(ast.from, false); + return (i, options) => handleForbidden(flatMap(mapError(from(i, options), (e) => new Transformation(ast, i, isDecoding ? "Encoded" : "Type", e)), (a) => flatMap(mapError(transform(a, options ?? defaultParseOption, ast, i), (e) => new Transformation(ast, i, "Transformation", e)), (i2) => mapError(to(i2, options), (e) => new Transformation(ast, i, isDecoding ? "Type" : "Encoded", e)))), ast, i, options); + } + case "Declaration": { + const parse = isDecoding ? ast.decodeUnknown(...ast.typeParameters) : ast.encodeUnknown(...ast.typeParameters); + return (i, options) => handleForbidden(parse(i, options ?? defaultParseOption, ast), ast, i, options); + } + case "Literal": return fromRefinement(ast, (u) => u === ast.literal); + case "UniqueSymbol": return fromRefinement(ast, (u) => u === ast.symbol); + case "UndefinedKeyword": return fromRefinement(ast, require_Layer.isUndefined); + case "NeverKeyword": return fromRefinement(ast, require_Layer.isNever); + case "UnknownKeyword": + case "AnyKeyword": + case "VoidKeyword": return require_Layer.right; + case "StringKeyword": return fromRefinement(ast, require_Layer.isString); + case "NumberKeyword": return fromRefinement(ast, require_Layer.isNumber); + case "BooleanKeyword": return fromRefinement(ast, require_Layer.isBoolean); + case "BigIntKeyword": return fromRefinement(ast, require_Layer.isBigInt); + case "SymbolKeyword": return fromRefinement(ast, require_Layer.isSymbol); + case "ObjectKeyword": return fromRefinement(ast, require_Layer.isObject); + case "Enums": return fromRefinement(ast, (u) => ast.enums.some(([_, value]) => value === u)); + case "TemplateLiteral": { + const regex = getTemplateLiteralRegExp(ast); + return fromRefinement(ast, (u) => require_Layer.isString(u) && regex.test(u)); + } + case "TupleType": { + const elements = ast.elements.map((e) => goMemo(e.type, isDecoding)); + const rest = ast.rest.map((annotatedAST) => goMemo(annotatedAST.type, isDecoding)); + let requiredTypes = ast.elements.filter((e) => !e.isOptional); + if (ast.rest.length > 0) requiredTypes = requiredTypes.concat(ast.rest.slice(1)); + const requiredLen = requiredTypes.length; + const expectedIndexes = ast.elements.length > 0 ? ast.elements.map((_, i) => i).join(" | ") : "never"; + const concurrency = getConcurrency(ast); + const batching = getBatching(ast); + return (input, options) => { + if (!require_Layer.isArray(input)) return require_Layer.left(new Type(ast, input)); + const allErrors = options?.errors === "all"; + const es = []; + let stepKey = 0; + const output = []; + const len = input.length; + for (let i = len; i <= requiredLen - 1; i++) { + const e = new Pointer(i, input, new Missing(requiredTypes[i - len])); + if (allErrors) { + es.push([stepKey++, e]); + continue; + } else return require_Layer.left(new Composite(ast, input, e, output)); + } + if (ast.rest.length === 0) for (let i = ast.elements.length; i <= len - 1; i++) { + const e = new Pointer(i, input, new Unexpected(input[i], `is unexpected, expected: ${expectedIndexes}`)); + if (allErrors) { + es.push([stepKey++, e]); + continue; + } else return require_Layer.left(new Composite(ast, input, e, output)); + } + let i = 0; + let queue = void 0; + for (; i < elements.length; i++) if (len < i + 1) { + if (ast.elements[i].isOptional) continue; + } else { + const parser = elements[i]; + const te = parser(input[i], options); + if (isEither(te)) { + if (require_Layer.isLeft(te)) { + const e = new Pointer(i, input, te.left); + if (allErrors) { + es.push([stepKey++, e]); + continue; + } else return require_Layer.left(new Composite(ast, input, e, sortByIndex(output))); + } + output.push([stepKey++, te.right]); + } else { + const nk = stepKey++; + const index = i; + if (!queue) queue = []; + queue.push(({ es, output }) => require_Layer.flatMap(require_Layer.either(te), (t) => { + if (require_Layer.isLeft(t)) { + const e = new Pointer(index, input, t.left); + if (allErrors) { + es.push([nk, e]); + return require_Layer._void; + } else return require_Layer.left(new Composite(ast, input, e, sortByIndex(output))); + } + output.push([nk, t.right]); + return require_Layer._void; + })); + } + } + if (require_Layer.isNonEmptyReadonlyArray(rest)) { + const [head, ...tail] = rest; + for (; i < len - tail.length; i++) { + const te = head(input[i], options); + if (isEither(te)) if (require_Layer.isLeft(te)) { + const e = new Pointer(i, input, te.left); + if (allErrors) { + es.push([stepKey++, e]); + continue; + } else return require_Layer.left(new Composite(ast, input, e, sortByIndex(output))); + } else output.push([stepKey++, te.right]); + else { + const nk = stepKey++; + const index = i; + if (!queue) queue = []; + queue.push(({ es, output }) => require_Layer.flatMap(require_Layer.either(te), (t) => { + if (require_Layer.isLeft(t)) { + const e = new Pointer(index, input, t.left); + if (allErrors) { + es.push([nk, e]); + return require_Layer._void; + } else return require_Layer.left(new Composite(ast, input, e, sortByIndex(output))); + } else { + output.push([nk, t.right]); + return require_Layer._void; + } + })); + } + } + for (let j = 0; j < tail.length; j++) { + const index = i + j; + if (len < index + 1) continue; + else { + const te = tail[j](input[index], options); + if (isEither(te)) { + if (require_Layer.isLeft(te)) { + const e = new Pointer(index, input, te.left); + if (allErrors) { + es.push([stepKey++, e]); + continue; + } else return require_Layer.left(new Composite(ast, input, e, sortByIndex(output))); + } + output.push([stepKey++, te.right]); + } else { + const nk = stepKey++; + if (!queue) queue = []; + queue.push(({ es, output }) => require_Layer.flatMap(require_Layer.either(te), (t) => { + if (require_Layer.isLeft(t)) { + const e = new Pointer(index, input, t.left); + if (allErrors) { + es.push([nk, e]); + return require_Layer._void; + } else return require_Layer.left(new Composite(ast, input, e, sortByIndex(output))); + } + output.push([nk, t.right]); + return require_Layer._void; + })); + } + } + } + } + const computeResult = ({ es, output }) => require_Layer.isNonEmptyArray(es) ? require_Layer.left(new Composite(ast, input, sortByIndex(es), sortByIndex(output))) : require_Layer.right(sortByIndex(output)); + if (queue && queue.length > 0) { + const cqueue = queue; + return require_Layer.suspend(() => { + const state = { + es: require_Layer.copy(es), + output: require_Layer.copy(output) + }; + return require_Layer.flatMap(require_Layer.forEach(cqueue, (f) => f(state), { + concurrency, + batching, + discard: true + }), () => computeResult(state)); + }); + } + return computeResult({ + output, + es + }); + }; + } + case "TypeLiteral": { + if (ast.propertySignatures.length === 0 && ast.indexSignatures.length === 0) return fromRefinement(ast, require_Layer.isNotNullable); + const propertySignatures = []; + const expectedKeysMap = {}; + const expectedKeys = []; + for (const ps of ast.propertySignatures) { + propertySignatures.push([goMemo(ps.type, isDecoding), ps]); + expectedKeysMap[ps.name] = null; + expectedKeys.push(ps.name); + } + const indexSignatures = ast.indexSignatures.map((is) => [ + goMemo(is.parameter, isDecoding), + goMemo(is.type, isDecoding), + is.parameter + ]); + const expectedAST = Union$1.make(ast.indexSignatures.map((is) => is.parameter).concat(expectedKeys.map((key) => require_Layer.isSymbol(key) ? new UniqueSymbol(key) : new Literal$1(key)))); + const expected = goMemo(expectedAST, isDecoding); + const concurrency = getConcurrency(ast); + const batching = getBatching(ast); + return (input, options) => { + if (!require_Layer.isRecord(input)) return require_Layer.left(new Type(ast, input)); + const allErrors = options?.errors === "all"; + const es = []; + let stepKey = 0; + const onExcessPropertyError = options?.onExcessProperty === "error"; + const onExcessPropertyPreserve = options?.onExcessProperty === "preserve"; + const output = {}; + let inputKeys; + if (onExcessPropertyError || onExcessPropertyPreserve) { + inputKeys = Reflect.ownKeys(input); + for (const key of inputKeys) { + const te = expected(key, options); + if (isEither(te) && require_Layer.isLeft(te)) if (onExcessPropertyError) { + const e = new Pointer(key, input, new Unexpected(input[key], `is unexpected, expected: ${String(expectedAST)}`)); + if (allErrors) { + es.push([stepKey++, e]); + continue; + } else return require_Layer.left(new Composite(ast, input, e, output)); + } else output[key] = input[key]; + } + } + let queue = void 0; + const isExact = options?.exact === true; + for (let i = 0; i < propertySignatures.length; i++) { + const ps = propertySignatures[i][1]; + const name = ps.name; + const hasKey = Object.prototype.hasOwnProperty.call(input, name); + if (!hasKey) { + if (ps.isOptional) continue; + else if (isExact) { + const e = new Pointer(name, input, new Missing(ps)); + if (allErrors) { + es.push([stepKey++, e]); + continue; + } else return require_Layer.left(new Composite(ast, input, e, output)); + } + } + const parser = propertySignatures[i][0]; + const te = parser(input[name], options); + if (isEither(te)) { + if (require_Layer.isLeft(te)) { + const e = new Pointer(name, input, hasKey ? te.left : new Missing(ps)); + if (allErrors) { + es.push([stepKey++, e]); + continue; + } else return require_Layer.left(new Composite(ast, input, e, output)); + } + output[name] = te.right; + } else { + const nk = stepKey++; + const index = name; + if (!queue) queue = []; + queue.push(({ es, output }) => require_Layer.flatMap(require_Layer.either(te), (t) => { + if (require_Layer.isLeft(t)) { + const e = new Pointer(index, input, hasKey ? t.left : new Missing(ps)); + if (allErrors) { + es.push([nk, e]); + return require_Layer._void; + } else return require_Layer.left(new Composite(ast, input, e, output)); + } + output[index] = t.right; + return require_Layer._void; + })); + } + } + for (let i = 0; i < indexSignatures.length; i++) { + const indexSignature = indexSignatures[i]; + const parameter = indexSignature[0]; + const type = indexSignature[1]; + const keys = getKeysForIndexSignature(input, indexSignature[2]); + for (const key of keys) { + const keu = parameter(key, options); + if (isEither(keu) && require_Layer.isRight(keu)) { + const vpr = type(input[key], options); + if (isEither(vpr)) { + if (require_Layer.isLeft(vpr)) { + const e = new Pointer(key, input, vpr.left); + if (allErrors) { + es.push([stepKey++, e]); + continue; + } else return require_Layer.left(new Composite(ast, input, e, output)); + } else if (!Object.prototype.hasOwnProperty.call(expectedKeysMap, key)) output[key] = vpr.right; + } else { + const nk = stepKey++; + const index = key; + if (!queue) queue = []; + queue.push(({ es, output }) => require_Layer.flatMap(require_Layer.either(vpr), (tv) => { + if (require_Layer.isLeft(tv)) { + const e = new Pointer(index, input, tv.left); + if (allErrors) { + es.push([nk, e]); + return require_Layer._void; + } else return require_Layer.left(new Composite(ast, input, e, output)); + } else { + if (!Object.prototype.hasOwnProperty.call(expectedKeysMap, key)) output[key] = tv.right; + return require_Layer._void; + } + })); + } + } + } + } + const computeResult = ({ es, output }) => { + if (require_Layer.isNonEmptyArray(es)) return require_Layer.left(new Composite(ast, input, sortByIndex(es), output)); + if (options?.propertyOrder === "original") { + const keys = inputKeys || Reflect.ownKeys(input); + for (const name of expectedKeys) if (keys.indexOf(name) === -1) keys.push(name); + const out = {}; + for (const key of keys) if (Object.prototype.hasOwnProperty.call(output, key)) out[key] = output[key]; + return require_Layer.right(out); + } + return require_Layer.right(output); + }; + if (queue && queue.length > 0) { + const cqueue = queue; + return require_Layer.suspend(() => { + const state = { + es: require_Layer.copy(es), + output: Object.assign({}, output) + }; + return require_Layer.flatMap(require_Layer.forEach(cqueue, (f) => f(state), { + concurrency, + batching, + discard: true + }), () => computeResult(state)); + }); + } + return computeResult({ + es, + output + }); + }; + } + case "Union": { + const searchTree = getSearchTree(ast.types, isDecoding); + const ownKeys = Reflect.ownKeys(searchTree.keys); + const ownKeysLen = ownKeys.length; + const astTypesLen = ast.types.length; + const map = /* @__PURE__ */ new Map(); + for (let i = 0; i < astTypesLen; i++) map.set(ast.types[i], goMemo(ast.types[i], isDecoding)); + const concurrency = getConcurrency(ast) ?? 1; + const batching = getBatching(ast); + return (input, options) => { + const es = []; + let stepKey = 0; + let candidates = []; + if (ownKeysLen > 0) if (require_Layer.isRecordOrArray(input)) for (let i = 0; i < ownKeysLen; i++) { + const name = ownKeys[i]; + const buckets = searchTree.keys[name].buckets; + if (Object.prototype.hasOwnProperty.call(input, name)) { + const literal = String(input[name]); + if (Object.prototype.hasOwnProperty.call(buckets, literal)) candidates = candidates.concat(buckets[literal]); + else { + const { candidates, literals } = searchTree.keys[name]; + const literalsUnion = Union$1.make(literals); + const errorAst = candidates.length === astTypesLen ? new TypeLiteral([new PropertySignature(name, literalsUnion, false, true)], []) : Union$1.make(candidates); + es.push([stepKey++, new Composite(errorAst, input, new Pointer(name, input, new Type(literalsUnion, input[name])))]); + } + } else { + const { candidates, literals } = searchTree.keys[name]; + const fakePropertySignature = new PropertySignature(name, Union$1.make(literals), false, true); + const errorAst = candidates.length === astTypesLen ? new TypeLiteral([fakePropertySignature], []) : Union$1.make(candidates); + es.push([stepKey++, new Composite(errorAst, input, new Pointer(name, input, new Missing(fakePropertySignature)))]); + } + } + else { + const errorAst = searchTree.candidates.length === astTypesLen ? ast : Union$1.make(searchTree.candidates); + es.push([stepKey++, new Type(errorAst, input)]); + } + if (searchTree.otherwise.length > 0) candidates = candidates.concat(searchTree.otherwise); + let queue = void 0; + for (let i = 0; i < candidates.length; i++) { + const candidate = candidates[i]; + const pr = map.get(candidate)(input, options); + if (isEither(pr) && (!queue || queue.length === 0)) if (require_Layer.isRight(pr)) return pr; + else es.push([stepKey++, pr.left]); + else { + const nk = stepKey++; + if (!queue) queue = []; + queue.push((state) => require_Layer.suspend(() => { + if ("finalResult" in state) return require_Layer._void; + else return require_Layer.flatMap(require_Layer.either(pr), (t) => { + if (require_Layer.isRight(t)) state.finalResult = t; + else state.es.push([nk, t.left]); + return require_Layer._void; + }); + })); + } + } + const computeResult = (es) => require_Layer.isNonEmptyArray(es) ? es.length === 1 && es[0][1]._tag === "Type" ? require_Layer.left(es[0][1]) : require_Layer.left(new Composite(ast, input, sortByIndex(es))) : require_Layer.left(new Type(ast, input)); + if (queue && queue.length > 0) { + const cqueue = queue; + return require_Layer.suspend(() => { + const state = { es: require_Layer.copy(es) }; + return require_Layer.flatMap(require_Layer.forEach(cqueue, (f) => f(state), { + concurrency, + batching, + discard: true + }), () => { + if ("finalResult" in state) return state.finalResult; + return computeResult(state.es); + }); + }); + } + return computeResult(es); + }; + } + case "Suspend": { + const get = memoizeThunk(() => goMemo(ast.f(), isDecoding)); + return (a, options) => get()(a, options); + } + } +}; +var fromRefinement = (ast, refinement) => (u) => refinement(u) ? require_Layer.right(u) : require_Layer.left(new Type(ast, u)); +/** @internal */ +var getLiterals = (ast, isDecoding) => { + switch (ast._tag) { + case "Declaration": { + const annotation = getSurrogateAnnotation(ast); + if (require_Layer.isSome(annotation)) return getLiterals(annotation.value, isDecoding); + break; + } + case "TypeLiteral": { + const out = []; + for (let i = 0; i < ast.propertySignatures.length; i++) { + const propertySignature = ast.propertySignatures[i]; + const type = isDecoding ? encodedAST(propertySignature.type) : typeAST(propertySignature.type); + if (isLiteral(type) && !propertySignature.isOptional) out.push([propertySignature.name, type]); + } + return out; + } + case "TupleType": { + const out = []; + for (let i = 0; i < ast.elements.length; i++) { + const element = ast.elements[i]; + const type = isDecoding ? encodedAST(element.type) : typeAST(element.type); + if (isLiteral(type) && !element.isOptional) out.push([i, type]); + } + return out; + } + case "Refinement": return getLiterals(ast.from, isDecoding); + case "Suspend": return getLiterals(ast.f(), isDecoding); + case "Transformation": return getLiterals(isDecoding ? ast.from : ast.to, isDecoding); + } + return []; +}; +/** +* The purpose of the algorithm is to narrow down the pool of possible +* candidates for decoding as much as possible. +* +* This function separates the schemas into two groups, `keys` and `otherwise`: +* +* - `keys`: the schema has at least one property with a literal value +* - `otherwise`: the schema has no properties with a literal value +* +* If a schema has at least one property with a literal value, so it ends up in +* `keys`, first a namespace is created for the name of the property containing +* the literal, and then within this namespace a "bucket" is created for the +* literal value in which to store all the schemas that have the same property +* and literal value. +* +* @internal +*/ +var getSearchTree = (members, isDecoding) => { + const keys = {}; + const otherwise = []; + const candidates = []; + for (let i = 0; i < members.length; i++) { + const member = members[i]; + const tags = getLiterals(member, isDecoding); + if (tags.length > 0) { + candidates.push(member); + for (let j = 0; j < tags.length; j++) { + const [key, literal] = tags[j]; + const hash = String(literal.literal); + keys[key] = keys[key] || { + buckets: {}, + literals: [], + candidates: [] + }; + const buckets = keys[key].buckets; + if (Object.prototype.hasOwnProperty.call(buckets, hash)) { + if (j < tags.length - 1) continue; + buckets[hash].push(member); + keys[key].literals.push(literal); + keys[key].candidates.push(member); + } else { + buckets[hash] = [member]; + keys[key].literals.push(literal); + keys[key].candidates.push(member); + break; + } + } + } else otherwise.push(member); + } + return { + keys, + otherwise, + candidates + }; +}; +var dropRightRefinement = (ast) => isRefinement$1(ast) ? dropRightRefinement(ast.from) : ast; +var handleForbidden = (effect, ast, actual, options) => { + if (options?.isEffectAllowed === true) return effect; + if (isEither(effect)) return effect; + const scheduler = new require_Layer.SyncScheduler(); + const fiber = require_Layer.runFork(effect, { scheduler }); + scheduler.flush(); + const exit = fiber.unsafePoll(); + if (exit) { + if (require_Layer.isSuccess(exit)) return require_Layer.right(exit.value); + const cause = exit.cause; + if (require_Layer.isFailType(cause)) return require_Layer.left(cause.error); + return require_Layer.left(new Forbidden(ast, actual, require_Layer.pretty(cause))); + } + return require_Layer.left(new Forbidden(ast, actual, "cannot be be resolved synchronously, this is caused by using runSync on an effect that performs async work")); +}; +var compare = ([a], [b]) => a > b ? 1 : a < b ? -1 : 0; +function sortByIndex(es) { + return es.sort(compare).map((t) => t[1]); +} +/** @internal */ +var getFinalTransformation = (transformation, isDecoding) => { + switch (transformation._tag) { + case "FinalTransformation": return isDecoding ? transformation.decode : transformation.encode; + case "ComposeTransformation": return require_Layer.right; + case "TypeLiteralTransformation": return (input) => { + let out = require_Layer.right(input); + for (const pst of transformation.propertySignatureTransformations) { + const [from, to] = isDecoding ? [pst.from, pst.to] : [pst.to, pst.from]; + const transformation = isDecoding ? pst.decode : pst.encode; + const f = (input) => { + const o = transformation(Object.prototype.hasOwnProperty.call(input, from) ? require_Layer.some(input[from]) : require_Layer.none$1()); + delete input[from]; + if (require_Layer.isSome(o)) input[to] = o.value; + return input; + }; + out = map(out, f); + } + return out; + }; + } +}; +var makeTree = (value, forest = []) => ({ + value, + forest +}); +/** +* @category formatting +* @since 3.10.0 +*/ +var TreeFormatter = { + formatIssue: (issue) => map(formatTree(issue), drawTree), + formatIssueSync: (issue) => { + const e = TreeFormatter.formatIssue(issue); + return isEither(e) ? require_Layer.getOrThrow(e) : require_Layer.runSync(e); + }, + formatError: (error) => TreeFormatter.formatIssue(error.issue), + formatErrorSync: (error) => TreeFormatter.formatIssueSync(error.issue) +}; +var drawTree = (tree) => tree.value + draw("\n", tree.forest); +var draw = (indentation, forest) => { + let r = ""; + const len = forest.length; + let tree; + for (let i = 0; i < len; i++) { + tree = forest[i]; + const isLast = i === len - 1; + r += indentation + (isLast ? "└" : "├") + "─ " + tree.value; + r += draw(indentation + (len > 1 && !isLast ? "│ " : " "), tree.forest); + } + return r; +}; +var formatTransformationKind = (kind) => { + switch (kind) { + case "Encoded": return "Encoded side transformation failure"; + case "Transformation": return "Transformation process failure"; + case "Type": return "Type side transformation failure"; + } +}; +var formatRefinementKind = (kind) => { + switch (kind) { + case "From": return "From side refinement failure"; + case "Predicate": return "Predicate refinement failure"; + } +}; +var getAnnotated = (issue) => "ast" in issue ? require_Layer.some(issue.ast) : require_Layer.none$1(); +var Either_void = /* @__PURE__ */ require_Layer.right(void 0); +var getCurrentMessage = (issue) => getAnnotated(issue).pipe(require_Layer.flatMap$2(getMessageAnnotation), require_Layer.match({ + onNone: () => Either_void, + onSome: (messageAnnotation) => { + const union = messageAnnotation(issue); + if (require_Layer.isString(union)) return require_Layer.right({ + message: union, + override: false + }); + if (require_Layer.isEffect(union)) return require_Layer.map(union, (message) => ({ + message, + override: false + })); + if (require_Layer.isString(union.message)) return require_Layer.right({ + message: union.message, + override: union.override + }); + return require_Layer.map(union.message, (message) => ({ + message, + override: union.override + })); + } +})); +var createParseIssueGuard = (tag) => (issue) => issue._tag === tag; +/** +* Returns `true` if the value is a `Composite`. +* +* @category guards +* @since 3.10.0 +*/ +var isComposite = /* @__PURE__ */ createParseIssueGuard("Composite"); +var isRefinement = /* @__PURE__ */ createParseIssueGuard("Refinement"); +var isTransformation = /* @__PURE__ */ createParseIssueGuard("Transformation"); +var getMessage = (issue) => flatMap(getCurrentMessage(issue), (currentMessage) => { + if (currentMessage !== void 0) return !currentMessage.override && (isComposite(issue) || isRefinement(issue) && issue.kind === "From" || isTransformation(issue) && issue.kind !== "Transformation") ? isTransformation(issue) || isRefinement(issue) ? getMessage(issue.issue) : Either_void : require_Layer.right(currentMessage.message); + return Either_void; +}); +var getParseIssueTitleAnnotation = (issue) => getAnnotated(issue).pipe(require_Layer.flatMap$2(getParseIssueTitleAnnotation$1), require_Layer.flatMapNullable((annotation) => annotation(issue)), require_Layer.getOrUndefined); +/** @internal */ +function getRefinementExpected(ast) { + return getDescriptionAnnotation(ast).pipe(require_Layer.orElse(() => getTitleAnnotation(ast)), require_Layer.orElse(() => getAutoTitleAnnotation(ast)), require_Layer.orElse(() => getIdentifierAnnotation(ast)), require_Layer.getOrElse(() => `{ ${ast.from} | filter }`)); +} +function getDefaultTypeMessage(issue) { + if (issue.message !== void 0) return issue.message; + return `Expected ${isRefinement$1(issue.ast) ? getRefinementExpected(issue.ast) : String(issue.ast)}, actual ${require_Layer.formatUnknown(issue.actual)}`; +} +var formatTypeMessage = (issue) => map(getMessage(issue), (message) => message ?? getParseIssueTitleAnnotation(issue) ?? getDefaultTypeMessage(issue)); +var getParseIssueTitle = (issue) => getParseIssueTitleAnnotation(issue) ?? String(issue.ast); +var formatForbiddenMessage = (issue) => issue.message ?? "is forbidden"; +var formatUnexpectedMessage = (issue) => issue.message ?? "is unexpected"; +var formatMissingMessage = (issue) => { + const missingMessageAnnotation = getMissingMessageAnnotation(issue.ast); + if (require_Layer.isSome(missingMessageAnnotation)) { + const annotation = missingMessageAnnotation.value(); + return require_Layer.isString(annotation) ? require_Layer.right(annotation) : annotation; + } + return require_Layer.right(issue.message ?? "is missing"); +}; +var formatTree = (issue) => { + switch (issue._tag) { + case "Type": return map(formatTypeMessage(issue), makeTree); + case "Forbidden": return require_Layer.right(makeTree(getParseIssueTitle(issue), [makeTree(formatForbiddenMessage(issue))])); + case "Unexpected": return require_Layer.right(makeTree(formatUnexpectedMessage(issue))); + case "Missing": return map(formatMissingMessage(issue), makeTree); + case "Transformation": return flatMap(getMessage(issue), (message) => { + if (message !== void 0) return require_Layer.right(makeTree(message)); + return map(formatTree(issue.issue), (tree) => makeTree(getParseIssueTitle(issue), [makeTree(formatTransformationKind(issue.kind), [tree])])); + }); + case "Refinement": return flatMap(getMessage(issue), (message) => { + if (message !== void 0) return require_Layer.right(makeTree(message)); + return map(formatTree(issue.issue), (tree) => makeTree(getParseIssueTitle(issue), [makeTree(formatRefinementKind(issue.kind), [tree])])); + }); + case "Pointer": return map(formatTree(issue.issue), (tree) => makeTree(formatPath(issue.path), [tree])); + case "Composite": return flatMap(getMessage(issue), (message) => { + if (message !== void 0) return require_Layer.right(makeTree(message)); + const parseIssueTitle = getParseIssueTitle(issue); + return isNonEmpty$1(issue.issues) ? map(require_Layer.forEach(issue.issues, formatTree), (forest) => makeTree(parseIssueTitle, forest)) : map(formatTree(issue.issues), (tree) => makeTree(parseIssueTitle, [tree])); + }); + } +}; +//#endregion +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Struct.js +/** +* Create a new object by picking properties of an existing object. +* +* @example +* ```ts +* import * as assert from "node:assert" +* import { pipe, Struct } from "effect" +* +* assert.deepStrictEqual(pipe({ a: "a", b: 1, c: true }, Struct.pick("a", "b")), { a: "a", b: 1 }) +* assert.deepStrictEqual(Struct.pick({ a: "a", b: 1, c: true }, "a", "b"), { a: "a", b: 1 }) +* ``` +* +* @since 2.0.0 +*/ +var pick = /* @__PURE__ */ require_Layer.dual((args) => require_Layer.isObject(args[0]), (s, ...keys) => { + const out = {}; + for (const k of keys) if (k in s) out[k] = s[k]; + return out; +}); +/** +* Create a new object by omitting properties of an existing object. +* +* @example +* ```ts +* import * as assert from "node:assert" +* import { pipe, Struct } from "effect" +* +* assert.deepStrictEqual(pipe({ a: "a", b: 1, c: true }, Struct.omit("c")), { a: "a", b: 1 }) +* assert.deepStrictEqual(Struct.omit({ a: "a", b: 1, c: true }, "c"), { a: "a", b: 1 }) +* ``` +* +* @since 2.0.0 +*/ +var omit = /* @__PURE__ */ require_Layer.dual((args) => require_Layer.isObject(args[0]), (s, ...keys) => { + const out = { ...s }; + for (const k of keys) delete out[k]; + return out; +}); +//#endregion +//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Schema.js +/** +* @since 3.10.0 +*/ +/** +* @since 3.10.0 +* @category symbol +*/ +var TypeId = /* @__PURE__ */ Symbol.for("effect/Schema"); +/** +* @category constructors +* @since 3.10.0 +*/ +function make(ast) { + return class SchemaClass { + [TypeId] = variance; + static ast = ast; + static annotations(annotations) { + return make(mergeSchemaAnnotations(this.ast, annotations)); + } + static pipe() { + return require_Layer.pipeArguments(this, arguments); + } + static toString() { + return String(ast); + } + static Type; + static Encoded; + static Context; + static [TypeId] = variance; + }; +} +var variance = { + /* c8 ignore next */ + _A: (_) => _, + /* c8 ignore next */ + _I: (_) => _, + /* c8 ignore next */ + _R: (_) => _ +}; +var builtInAnnotations = { + typeConstructor: TypeConstructorAnnotationId, + schemaId: SchemaIdAnnotationId, + message: MessageAnnotationId, + missingMessage: MissingMessageAnnotationId, + identifier: IdentifierAnnotationId, + title: TitleAnnotationId, + description: DescriptionAnnotationId, + examples: ExamplesAnnotationId, + default: DefaultAnnotationId, + documentation: DocumentationAnnotationId, + jsonSchema: JSONSchemaAnnotationId, + arbitrary: ArbitraryAnnotationId, + pretty: PrettyAnnotationId, + equivalence: EquivalenceAnnotationId, + concurrency: ConcurrencyAnnotationId, + batching: BatchingAnnotationId, + parseIssueTitle: ParseIssueTitleAnnotationId, + parseOptions: ParseOptionsAnnotationId, + decodingFallback: DecodingFallbackAnnotationId +}; +var toASTAnnotations = (annotations) => { + if (!annotations) return {}; + const out = { ...annotations }; + for (const key in builtInAnnotations) if (key in annotations) { + const id = builtInAnnotations[key]; + out[id] = annotations[key]; + delete out[key]; + } + return out; +}; +var mergeSchemaAnnotations = (ast, annotations$3) => annotations(ast, toASTAnnotations(annotations$3)); +/** +* @category formatting +* @since 3.10.0 +*/ +var format = (schema) => String(schema.ast); +/** +* @category decoding +* @since 3.10.0 +*/ +var decodeUnknownEither = (schema, options) => { + const decodeUnknownEither = decodeUnknownEither$1(schema, options); + return (u, overrideOptions) => require_Layer.mapLeft(decodeUnknownEither(u, overrideOptions), parseError); +}; +/** +* Tests if a value is a `Schema`. +* +* @category guards +* @since 3.10.0 +*/ +var isSchema = (u) => require_Layer.hasProperty(u, TypeId) && require_Layer.isObject(u[TypeId]); +function getDefaultLiteralAST(literals) { + return isMembers(literals) ? Union$1.make(mapMembers(literals, (literal) => new Literal$1(literal))) : new Literal$1(literals[0]); +} +function makeLiteralClass(literals, ast = getDefaultLiteralAST(literals)) { + return class LiteralClass extends make(ast) { + static annotations(annotations) { + return makeLiteralClass(this.literals, mergeSchemaAnnotations(this.ast, annotations)); + } + static literals = [...literals]; + }; +} +function Literal(...literals) { + return require_Layer.isNonEmptyReadonlyArray(literals) ? makeLiteralClass(literals) : Never; +} +var declareConstructor = (typeParameters, options, annotations) => makeDeclareClass(typeParameters, new Declaration(typeParameters.map((tp) => tp.ast), (...typeParameters) => options.decode(...typeParameters.map(make)), (...typeParameters) => options.encode(...typeParameters.map(make)), toASTAnnotations(annotations))); +var declarePrimitive = (is, annotations) => { + const decodeUnknown = () => (input, _, ast) => is(input) ? succeed(input) : fail(new Type(ast, input)); + return makeDeclareClass([], new Declaration([], decodeUnknown, decodeUnknown, toASTAnnotations(annotations))); +}; +function makeDeclareClass(typeParameters, ast) { + return class DeclareClass extends make(ast) { + static annotations(annotations) { + return makeDeclareClass(this.typeParameters, mergeSchemaAnnotations(this.ast, annotations)); + } + static typeParameters = [...typeParameters]; + }; +} +/** +* The constraint `R extends Schema.Context` enforces dependencies solely from `typeParameters`. +* This ensures that when you call `Schema.to` or `Schema.from`, you receive a schema with a `never` context. +* +* @category constructors +* @since 3.10.0 +*/ +var declare = function() { + if (Array.isArray(arguments[0])) { + const typeParameters = arguments[0]; + const options = arguments[1]; + const annotations = arguments[2]; + return declareConstructor(typeParameters, options, annotations); + } + const is = arguments[0]; + const annotations = arguments[1]; + return declarePrimitive(is, annotations); +}; +/** +* @category schema id +* @since 3.10.0 +*/ +var InstanceOfSchemaId = /* @__PURE__ */ Symbol.for("effect/SchemaId/InstanceOf"); +/** +* @category constructors +* @since 3.10.0 +*/ +var instanceOf = (constructor, annotations) => declare((u) => u instanceof constructor, { + title: constructor.name, + description: `an instance of ${constructor.name}`, + pretty: () => String, + schemaId: InstanceOfSchemaId, + [InstanceOfSchemaId]: { constructor }, + ...annotations +}); +/** +* @category primitives +* @since 3.10.0 +*/ +var Undefined = class extends make(undefinedKeyword) {}; +/** +* @category primitives +* @since 3.10.0 +*/ +var Null = class extends make($null) {}; +/** +* @category primitives +* @since 3.10.0 +*/ +var Never = class extends make(neverKeyword) {}; +/** +* @category primitives +* @since 3.10.0 +*/ +var Unknown = class extends make(unknownKeyword) {}; +/** +* @category primitives +* @since 3.10.0 +*/ +var BigIntFromSelf = class extends make(bigIntKeyword) {}; +/** +* @category primitives +* @since 3.10.0 +*/ +var SymbolFromSelf = class extends make(symbolKeyword) {}; +/** @ignore */ +var String$ = class extends make(stringKeyword) {}; +/** @ignore */ +var Number$ = class extends make(numberKeyword) {}; +/** @ignore */ +var Boolean$ = class extends make(booleanKeyword) {}; +var getDefaultUnionAST = (members) => Union$1.make(members.map((m) => m.ast)); +function makeUnionClass(members, ast = getDefaultUnionAST(members)) { + return class UnionClass extends make(ast) { + static annotations(annotations) { + return makeUnionClass(this.members, mergeSchemaAnnotations(this.ast, annotations)); + } + static members = [...members]; + }; +} +function Union(...members) { + return isMembers(members) ? makeUnionClass(members) : require_Layer.isNonEmptyReadonlyArray(members) ? members[0] : Never; +} +/** +* @category combinators +* @since 3.10.0 +*/ +var UndefinedOr = (self) => Union(self, Undefined); +/** +* @since 3.10.0 +*/ +var element = (self) => new ElementImpl(new OptionalType(self.ast, false), self); +var ElementImpl = class ElementImpl { + ast; + from; + [TypeId]; + _Token; + constructor(ast, from) { + this.ast = ast; + this.from = from; + } + annotations(annotations) { + return new ElementImpl(new OptionalType(this.ast.type, this.ast.isOptional, { + ...this.ast.annotations, + ...toASTAnnotations(annotations) + }), this.from); + } + toString() { + return `${this.ast.type}${this.ast.isOptional ? "?" : ""}`; + } +}; +var getDefaultTupleTypeAST = (elements, rest) => new TupleType(elements.map((el) => isSchema(el) ? new OptionalType(el.ast, false) : el.ast), rest.map((el) => isSchema(el) ? new Type$1(el.ast) : el.ast), true); +function makeTupleTypeClass(elements, rest, ast = getDefaultTupleTypeAST(elements, rest)) { + return class TupleTypeClass extends make(ast) { + static annotations(annotations) { + return makeTupleTypeClass(this.elements, this.rest, mergeSchemaAnnotations(this.ast, annotations)); + } + static elements = [...elements]; + static rest = [...rest]; + }; +} +function Tuple(...args) { + return Array.isArray(args[0]) ? makeTupleTypeClass(args[0], args.slice(1)) : makeTupleTypeClass(args, []); +} +function makeArrayClass(value, ast) { + return class ArrayClass extends makeTupleTypeClass([], [value], ast) { + static annotations(annotations) { + return makeArrayClass(this.value, mergeSchemaAnnotations(this.ast, annotations)); + } + static value = value; + }; +} +var Array$ = (value) => makeArrayClass(value); +var formatPropertySignatureToken = (isOptional) => isOptional ? "\"?:\"" : "\":\""; +/** +* @category PropertySignature +* @since 3.10.0 +*/ +var PropertySignatureDeclaration = class extends OptionalType { + isReadonly; + defaultValue; + /** + * @since 3.10.0 + */ + _tag = "PropertySignatureDeclaration"; + constructor(type, isOptional, isReadonly, annotations, defaultValue) { + super(type, isOptional, annotations); + this.isReadonly = isReadonly; + this.defaultValue = defaultValue; + } + /** + * @since 3.10.0 + */ + toString() { + const token = formatPropertySignatureToken(this.isOptional); + const type = String(this.type); + return `PropertySignature<${token}, ${type}, never, ${token}, ${type}>`; + } +}; +/** +* @category PropertySignature +* @since 3.10.0 +*/ +var ToPropertySignature = class extends OptionalType { + isReadonly; + defaultValue; + constructor(type, isOptional, isReadonly, annotations, defaultValue) { + super(type, isOptional, annotations); + this.isReadonly = isReadonly; + this.defaultValue = defaultValue; + } +}; +var formatPropertyKey = (p) => { + if (p === void 0) return "never"; + if (require_Layer.isString(p)) return JSON.stringify(p); + return String(p); +}; +/** +* @category PropertySignature +* @since 3.10.0 +*/ +var PropertySignatureTransformation = class { + from; + to; + decode; + encode; + /** + * @since 3.10.0 + */ + _tag = "PropertySignatureTransformation"; + constructor(from, to, decode, encode) { + this.from = from; + this.to = to; + this.decode = decode; + this.encode = encode; + } + /** + * @since 3.10.0 + */ + toString() { + return `PropertySignature<${formatPropertySignatureToken(this.to.isOptional)}, ${this.to.type}, ${formatPropertyKey(this.from.fromKey)}, ${formatPropertySignatureToken(this.from.isOptional)}, ${this.from.type}>`; + } +}; +var mergeSignatureAnnotations = (ast, annotations) => { + switch (ast._tag) { + case "PropertySignatureDeclaration": return new PropertySignatureDeclaration(ast.type, ast.isOptional, ast.isReadonly, { + ...ast.annotations, + ...annotations + }, ast.defaultValue); + case "PropertySignatureTransformation": return new PropertySignatureTransformation(ast.from, new ToPropertySignature(ast.to.type, ast.to.isOptional, ast.to.isReadonly, { + ...ast.to.annotations, + ...annotations + }, ast.to.defaultValue), ast.decode, ast.encode); + } +}; +/** +* @since 3.10.0 +* @category symbol +*/ +var PropertySignatureTypeId = /* @__PURE__ */ Symbol.for("effect/PropertySignature"); +/** +* @since 3.10.0 +* @category guards +*/ +var isPropertySignature = (u) => require_Layer.hasProperty(u, PropertySignatureTypeId); +var PropertySignatureImpl = class PropertySignatureImpl { + ast; + [TypeId]; + [PropertySignatureTypeId] = null; + _TypeToken; + _Key; + _EncodedToken; + _HasDefault; + constructor(ast) { + this.ast = ast; + } + pipe() { + return require_Layer.pipeArguments(this, arguments); + } + annotations(annotations) { + return new PropertySignatureImpl(mergeSignatureAnnotations(this.ast, toASTAnnotations(annotations))); + } + toString() { + return String(this.ast); + } +}; +/** +* @category PropertySignature +* @since 3.10.0 +*/ +var makePropertySignature = (ast) => new PropertySignatureImpl(ast); +var PropertySignatureWithFromImpl = class PropertySignatureWithFromImpl extends PropertySignatureImpl { + from; + constructor(ast, from) { + super(ast); + this.from = from; + } + annotations(annotations) { + return new PropertySignatureWithFromImpl(mergeSignatureAnnotations(this.ast, toASTAnnotations(annotations)), this.from); + } +}; +/** +* Lifts a `Schema` into a `PropertySignature`. +* +* @category PropertySignature +* @since 3.10.0 +*/ +var propertySignature = (self) => new PropertySignatureWithFromImpl(new PropertySignatureDeclaration(self.ast, false, true, {}, void 0), self); +/** +* Enhances a property signature with a default constructor value. +* +* @category PropertySignature +* @since 3.10.0 +*/ +var withConstructorDefault = /* @__PURE__ */ require_Layer.dual(2, (self, defaultValue) => { + const ast = self.ast; + switch (ast._tag) { + case "PropertySignatureDeclaration": return makePropertySignature(new PropertySignatureDeclaration(ast.type, ast.isOptional, ast.isReadonly, ast.annotations, defaultValue)); + case "PropertySignatureTransformation": return makePropertySignature(new PropertySignatureTransformation(ast.from, new ToPropertySignature(ast.to.type, ast.to.isOptional, ast.to.isReadonly, ast.to.annotations, defaultValue), ast.decode, ast.encode)); + } +}); +/** +* @category PropertySignature +* @since 3.10.0 +*/ +var optional = (self) => { + return new PropertySignatureWithFromImpl(new PropertySignatureDeclaration(self.ast === undefinedKeyword || self.ast === neverKeyword ? undefinedKeyword : UndefinedOr(self).ast, true, true, {}, void 0), self); +}; +var preserveMissingMessageAnnotation = /* @__PURE__ */ pickAnnotations([MissingMessageAnnotationId]); +var getDefaultTypeLiteralAST = (fields, records) => { + const ownKeys = Reflect.ownKeys(fields); + const pss = []; + if (ownKeys.length > 0) { + const from = []; + const to = []; + const transformations = []; + for (let i = 0; i < ownKeys.length; i++) { + const key = ownKeys[i]; + const field = fields[key]; + if (isPropertySignature(field)) { + const ast = field.ast; + switch (ast._tag) { + case "PropertySignatureDeclaration": { + const type = ast.type; + const isOptional = ast.isOptional; + const toAnnotations = ast.annotations; + from.push(new PropertySignature(key, type, isOptional, true, preserveMissingMessageAnnotation(ast))); + to.push(new PropertySignature(key, typeAST(type), isOptional, true, toAnnotations)); + pss.push(new PropertySignature(key, type, isOptional, true, toAnnotations)); + break; + } + case "PropertySignatureTransformation": { + const fromKey = ast.from.fromKey ?? key; + from.push(new PropertySignature(fromKey, ast.from.type, ast.from.isOptional, true, ast.from.annotations)); + to.push(new PropertySignature(key, ast.to.type, ast.to.isOptional, true, ast.to.annotations)); + transformations.push(new PropertySignatureTransformation$1(fromKey, key, ast.decode, ast.encode)); + break; + } + } + } else { + from.push(new PropertySignature(key, field.ast, false, true)); + to.push(new PropertySignature(key, typeAST(field.ast), false, true)); + pss.push(new PropertySignature(key, field.ast, false, true)); + } + } + if (require_Layer.isNonEmptyReadonlyArray(transformations)) { + const issFrom = []; + const issTo = []; + for (const r of records) { + const { indexSignatures, propertySignatures } = record(r.key.ast, r.value.ast); + propertySignatures.forEach((ps) => { + from.push(ps); + to.push(new PropertySignature(ps.name, typeAST(ps.type), ps.isOptional, ps.isReadonly, ps.annotations)); + }); + indexSignatures.forEach((is) => { + issFrom.push(is); + issTo.push(new IndexSignature(is.parameter, typeAST(is.type), is.isReadonly)); + }); + } + return new Transformation$1(new TypeLiteral(from, issFrom, { [AutoTitleAnnotationId]: "Struct (Encoded side)" }), new TypeLiteral(to, issTo, { [AutoTitleAnnotationId]: "Struct (Type side)" }), new TypeLiteralTransformation(transformations)); + } + } + const iss = []; + for (const r of records) { + const { indexSignatures, propertySignatures } = record(r.key.ast, r.value.ast); + propertySignatures.forEach((ps) => pss.push(ps)); + indexSignatures.forEach((is) => iss.push(is)); + } + return new TypeLiteral(pss, iss); +}; +var lazilyMergeDefaults = (fields, out) => { + const ownKeys = Reflect.ownKeys(fields); + for (const key of ownKeys) { + const field = fields[key]; + if (out[key] === void 0 && isPropertySignature(field)) { + const ast = field.ast; + const defaultValue = ast._tag === "PropertySignatureDeclaration" ? ast.defaultValue : ast.to.defaultValue; + if (defaultValue !== void 0) out[key] = defaultValue(); + } + } + return out; +}; +function makeTypeLiteralClass(fields, records, ast = getDefaultTypeLiteralAST(fields, records)) { + return class TypeLiteralClass extends make(ast) { + static annotations(annotations) { + return makeTypeLiteralClass(this.fields, this.records, mergeSchemaAnnotations(this.ast, annotations)); + } + static fields = { ...fields }; + static records = [...records]; + static make = (props, options) => { + const propsWithDefaults = lazilyMergeDefaults(fields, { ...props }); + return getDisableValidationMakeOption(options) ? propsWithDefaults : validateSync(this)(propsWithDefaults); + }; + static pick(...keys) { + return Struct(pick(fields, ...keys)); + } + static omit(...keys) { + return Struct(omit(fields, ...keys)); + } + }; +} +function Struct(fields, ...records) { + return makeTypeLiteralClass(fields, records); +} +/** +* Returns a property signature that represents a tag. +* A tag is a literal value that is used to distinguish between different types of objects. +* The tag is optional when using the `make` method. +* +* @example +* ```ts +* import * as assert from "node:assert" +* import { Schema } from "effect" +* +* const User = Schema.Struct({ +* _tag: Schema.tag("User"), +* name: Schema.String, +* age: Schema.Number +* }) +* +* assert.deepStrictEqual(User.make({ name: "John", age: 44 }), { _tag: "User", name: "John", age: 44 }) +* ``` +* +* @see {@link TaggedStruct} +* +* @since 3.10.0 +*/ +var tag = (tag) => Literal(tag).pipe(propertySignature, withConstructorDefault(() => tag)); +/** +* A tagged struct is a struct that has a tag property that is used to distinguish between different types of objects. +* +* The tag is optional when using the `make` method. +* +* @example +* ```ts +* import * as assert from "node:assert" +* import { Schema } from "effect" +* +* const User = Schema.TaggedStruct("User", { +* name: Schema.String, +* age: Schema.Number +* }) +* +* assert.deepStrictEqual(User.make({ name: "John", age: 44 }), { _tag: "User", name: "John", age: 44 }) +* ``` +* +* @category constructors +* @since 3.10.0 +*/ +var TaggedStruct = (value, fields) => Struct({ + _tag: tag(value), + ...fields +}); +/** +* @category constructors +* @since 3.10.0 +*/ +var suspend = (f) => make(new Suspend(() => f().ast)); +/** +* @since 3.10.0 +* @category symbol +*/ +var RefineSchemaId = /* @__PURE__ */ Symbol.for("effect/SchemaId/Refine"); +function makeRefineClass(from, filter, ast) { + return class RefineClass extends make(ast) { + static annotations(annotations) { + return makeRefineClass(this.from, this.filter, mergeSchemaAnnotations(this.ast, annotations)); + } + static [RefineSchemaId] = from; + static from = from; + static filter = filter; + static make = (a, options) => { + return getDisableValidationMakeOption(options) ? a : validateSync(this)(a); + }; + }; +} +var fromFilterPredicateReturnTypeItem = (item, ast, input) => { + if (require_Layer.isBoolean(item)) return item ? require_Layer.none$1() : require_Layer.some(new Type(ast, input)); + if (require_Layer.isString(item)) return require_Layer.some(new Type(ast, input, item)); + if (item !== void 0) { + if ("_tag" in item) return require_Layer.some(item); + const issue = new Type(ast, input, item.message); + return require_Layer.some(require_Layer.isNonEmptyReadonlyArray(item.path) ? new Pointer(item.path, input, issue) : issue); + } + return require_Layer.none$1(); +}; +var toFilterParseIssue = (out, ast, input) => { + if (isSingle(out)) return fromFilterPredicateReturnTypeItem(out, ast, input); + if (require_Layer.isNonEmptyReadonlyArray(out)) { + const issues = require_Layer.filterMap(out, (issue) => fromFilterPredicateReturnTypeItem(issue, ast, input)); + if (require_Layer.isNonEmptyReadonlyArray(issues)) return require_Layer.some(issues.length === 1 ? issues[0] : new Composite(ast, input, issues)); + } + return require_Layer.none$1(); +}; +function filter(predicate, annotations) { + return (self) => { + function filter(input, options, ast) { + return toFilterParseIssue(predicate(input, options, ast), ast, input); + } + return makeRefineClass(self, filter, new Refinement$1(self.ast, filter, toASTAnnotations(annotations))); + }; +} +function makeTransformationClass(from, to, ast) { + return class TransformationClass extends make(ast) { + static annotations(annotations) { + return makeTransformationClass(this.from, this.to, mergeSchemaAnnotations(this.ast, annotations)); + } + static from = from; + static to = to; + }; +} +/** +* Create a new `Schema` by transforming the input and output of an existing `Schema` +* using the provided decoding functions. +* +* @category transformations +* @since 3.10.0 +*/ +var transformOrFail = /* @__PURE__ */ require_Layer.dual((args) => isSchema(args[0]) && isSchema(args[1]), (from, to, options) => makeTransformationClass(from, to, new Transformation$1(from.ast, to.ast, new FinalTransformation(options.decode, options.encode)))); +/** +* Create a new `Schema` by transforming the input and output of an existing `Schema` +* using the provided mapping functions. +* +* @category transformations +* @since 3.10.0 +*/ +var transform = /* @__PURE__ */ require_Layer.dual((args) => isSchema(args[0]) && isSchema(args[1]), (from, to, options) => transformOrFail(from, to, { + strict: true, + decode: (fromA, _options, _ast, toA) => succeed(options.decode(fromA, toA)), + encode: (toI, _options, _ast, toA) => succeed(options.encode(toI, toA)) +})); +/** +* @category schema id +* @since 3.10.0 +*/ +var TrimmedSchemaId = /* @__PURE__ */ Symbol.for("effect/SchemaId/Trimmed"); +/** +* Verifies that a string contains no leading or trailing whitespaces. +* +* Note. This combinator does not make any transformations, it only validates. +* If what you were looking for was a combinator to trim strings, then check out the `trim` combinator. +* +* @category string filters +* @since 3.10.0 +*/ +var trimmed = (annotations) => (self) => self.pipe(filter((a) => a === a.trim(), { + schemaId: TrimmedSchemaId, + title: "trimmed", + description: "a string with no leading or trailing whitespace", + jsonSchema: { pattern: "^\\S[\\s\\S]*\\S$|^\\S$|^$" }, + ...annotations +})); +/** +* @category schema id +* @since 3.10.0 +*/ +var MinLengthSchemaId = MinLengthSchemaId$1; +/** +* @category string filters +* @since 3.10.0 +*/ +var minLength = (minLength, annotations) => (self) => self.pipe(filter((a) => a.length >= minLength, { + schemaId: MinLengthSchemaId, + title: `minLength(${minLength})`, + description: `a string at least ${minLength} character(s) long`, + jsonSchema: { minLength }, + ...annotations +})); +/** +* @category schema id +* @since 3.10.0 +*/ +var LengthSchemaId = LengthSchemaId$1; +/** +* @category string filters +* @since 3.10.0 +*/ +var length = (length, annotations) => (self) => { + const minLength = require_Layer.isObject(length) ? Math.max(0, Math.floor(length.min)) : Math.max(0, Math.floor(length)); + const maxLength = require_Layer.isObject(length) ? Math.max(minLength, Math.floor(length.max)) : minLength; + if (minLength !== maxLength) return self.pipe(filter((a) => a.length >= minLength && a.length <= maxLength, { + schemaId: LengthSchemaId, + title: `length({ min: ${minLength}, max: ${maxLength})`, + description: `a string at least ${minLength} character(s) and at most ${maxLength} character(s) long`, + jsonSchema: { + minLength, + maxLength + }, + ...annotations + })); + return self.pipe(filter((a) => a.length === minLength, { + schemaId: LengthSchemaId, + title: `length(${minLength})`, + description: minLength === 1 ? `a single character` : `a string ${minLength} character(s) long`, + jsonSchema: { + minLength, + maxLength: minLength + }, + ...annotations + })); +}; +/** +* @category schema id +* @since 3.10.0 +*/ +var PatternSchemaId = /* @__PURE__ */ Symbol.for("effect/SchemaId/Pattern"); +/** +* @category string filters +* @since 3.10.0 +*/ +var pattern = (regex, annotations) => (self) => { + const source = regex.source; + return self.pipe(filter((a) => { + regex.lastIndex = 0; + return regex.test(a); + }, { + schemaId: PatternSchemaId, + [PatternSchemaId]: { regex }, + description: `a string matching the pattern ${source}`, + jsonSchema: { pattern: source }, + ...annotations + })); +}; +/** +* @category schema id +* @since 3.10.0 +*/ +var LowercasedSchemaId = /* @__PURE__ */ Symbol.for("effect/SchemaId/Lowercased"); +/** +* Verifies that a string is lowercased. +* +* @category string filters +* @since 3.10.0 +*/ +var lowercased = (annotations) => (self) => self.pipe(filter((a) => a === a.toLowerCase(), { + schemaId: LowercasedSchemaId, + title: "lowercased", + description: "a lowercase string", + jsonSchema: { pattern: "^[^A-Z]*$" }, + ...annotations +})); +/** +* @category string constructors +* @since 3.10.0 +*/ +var Lowercased = class extends String$.pipe(/* @__PURE__ */ lowercased({ identifier: "Lowercased" })) {}; +/** +* @category schema id +* @since 3.10.0 +*/ +var UppercasedSchemaId = /* @__PURE__ */ Symbol.for("effect/SchemaId/Uppercased"); +/** +* Verifies that a string is uppercased. +* +* @category string filters +* @since 3.10.0 +*/ +var uppercased = (annotations) => (self) => self.pipe(filter((a) => a === a.toUpperCase(), { + schemaId: UppercasedSchemaId, + title: "uppercased", + description: "an uppercase string", + jsonSchema: { pattern: "^[^a-z]*$" }, + ...annotations +})); +/** +* @category string constructors +* @since 3.10.0 +*/ +var Uppercased = class extends String$.pipe(/* @__PURE__ */ uppercased({ identifier: "Uppercased" })) {}; +/** +* @category schema id +* @since 3.10.0 +*/ +var CapitalizedSchemaId = /* @__PURE__ */ Symbol.for("effect/SchemaId/Capitalized"); +/** +* Verifies that a string is capitalized. +* +* @category string filters +* @since 3.10.0 +*/ +var capitalized = (annotations) => (self) => self.pipe(filter((a) => a[0]?.toUpperCase() === a[0], { + schemaId: CapitalizedSchemaId, + title: "capitalized", + description: "a capitalized string", + jsonSchema: { pattern: "^[^a-z]?.*$" }, + ...annotations +})); +/** +* @category string constructors +* @since 3.10.0 +*/ +var Capitalized = class extends String$.pipe(/* @__PURE__ */ capitalized({ identifier: "Capitalized" })) {}; +/** +* @category schema id +* @since 3.10.0 +*/ +var UncapitalizedSchemaId = /* @__PURE__ */ Symbol.for("effect/SchemaId/Uncapitalized"); +/** +* Verifies that a string is uncapitalized. +* +* @category string filters +* @since 3.10.0 +*/ +var uncapitalized = (annotations) => (self) => self.pipe(filter((a) => a[0]?.toLowerCase() === a[0], { + schemaId: UncapitalizedSchemaId, + title: "uncapitalized", + description: "a uncapitalized string", + jsonSchema: { pattern: "^[^A-Z]?.*$" }, + ...annotations +})); +/** +* @category string constructors +* @since 3.10.0 +*/ +var Uncapitalized = class extends String$.pipe(/* @__PURE__ */ uncapitalized({ identifier: "Uncapitalized" })) {}; +String$.pipe(/* @__PURE__ */ length(1, { identifier: "Char" })); +/** +* @category string filters +* @since 3.10.0 +*/ +var nonEmptyString = (annotations) => minLength(1, { + title: "nonEmptyString", + description: "a non empty string", + ...annotations +}); +transform(String$.annotations({ description: "a string that will be converted to lowercase" }), Lowercased, { + strict: true, + decode: (i) => i.toLowerCase(), + encode: require_Layer.identity +}).annotations({ identifier: "Lowercase" }); +transform(String$.annotations({ description: "a string that will be converted to uppercase" }), Uppercased, { + strict: true, + decode: (i) => i.toUpperCase(), + encode: require_Layer.identity +}).annotations({ identifier: "Uppercase" }); +transform(String$.annotations({ description: "a string that will be converted to a capitalized format" }), Capitalized, { + strict: true, + decode: (i) => capitalize(i), + encode: require_Layer.identity +}).annotations({ identifier: "Capitalize" }); +transform(String$.annotations({ description: "a string that will be converted to an uncapitalized format" }), Uncapitalized, { + strict: true, + decode: (i) => uncapitalize(i), + encode: require_Layer.identity +}).annotations({ identifier: "Uncapitalize" }); +/** +* @category string constructors +* @since 3.10.0 +*/ +var Trimmed = class extends String$.pipe(/* @__PURE__ */ trimmed({ identifier: "Trimmed" })) {}; +/** +* Useful for validating strings that must contain meaningful characters without +* leading or trailing whitespace. +* +* @example +* ```ts +* import { Schema } from "effect" +* +* console.log(Schema.decodeOption(Schema.NonEmptyTrimmedString)("")) // Option.none() +* console.log(Schema.decodeOption(Schema.NonEmptyTrimmedString)(" a ")) // Option.none() +* console.log(Schema.decodeOption(Schema.NonEmptyTrimmedString)("a")) // Option.some("a") +* ``` +* +* @category string constructors +* @since 3.10.0 +*/ +var NonEmptyTrimmedString = class extends Trimmed.pipe(/* @__PURE__ */ nonEmptyString({ identifier: "NonEmptyTrimmedString" })) {}; +transform(String$.annotations({ description: "a string that will be trimmed" }), Trimmed, { + strict: true, + decode: (i) => i.trim(), + encode: require_Layer.identity +}).annotations({ identifier: "Trim" }); +var getErrorMessage = (e) => e instanceof Error ? e.message : String(e); +String$.pipe(/* @__PURE__ */ nonEmptyString({ identifier: "NonEmptyString" })); +/** +* @category schema id +* @since 3.10.0 +*/ +var UUIDSchemaId = /* @__PURE__ */ Symbol.for("effect/SchemaId/UUID"); +var uuidRegexp = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/i; +String$.pipe(/* @__PURE__ */ pattern(uuidRegexp, { + schemaId: UUIDSchemaId, + identifier: "UUID", + jsonSchema: { + format: "uuid", + pattern: uuidRegexp.source + }, + description: "a Universally Unique Identifier", + arbitrary: () => (fc) => fc.uuid() +})); +/** +* @category schema id +* @since 3.10.0 +*/ +var ULIDSchemaId = /* @__PURE__ */ Symbol.for("effect/SchemaId/ULID"); +String$.pipe(/* @__PURE__ */ pattern(/^[0-7][0-9A-HJKMNP-TV-Z]{25}$/i, { + schemaId: ULIDSchemaId, + identifier: "ULID", + description: "a Universally Unique Lexicographically Sortable Identifier", + arbitrary: () => (fc) => fc.ulid() +})); +/** +* Defines a schema that represents a `URL` object. +* +* @category URL constructors +* @since 3.11.0 +*/ +var URLFromSelf = class extends instanceOf(URL, { + typeConstructor: { _tag: "URL" }, + identifier: "URLFromSelf", + arbitrary: () => (fc) => fc.webUrl().map((s) => new URL(s)), + pretty: () => (url) => url.toString() +}) {}; +transformOrFail(String$.annotations({ description: "a string to be decoded into a URL" }), URLFromSelf, { + strict: true, + decode: (i, _, ast) => _try({ + try: () => new URL(i), + catch: (e) => new Type(ast, i, `Unable to decode ${JSON.stringify(i)} into a URL. ${getErrorMessage(e)}`) + }), + encode: (a) => succeed(a.toString()) +}).annotations({ + identifier: "URL", + pretty: () => (url) => url.toString() +}); +/** +* @category schema id +* @since 3.10.0 +*/ +var FiniteSchemaId = FiniteSchemaId$1; +/** +* Ensures that the provided value is a finite number (excluding NaN, +Infinity, and -Infinity). +* +* @category number filters +* @since 3.10.0 +*/ +var finite = (annotations) => (self) => self.pipe(filter(Number.isFinite, { + schemaId: FiniteSchemaId, + title: "finite", + description: "a finite number", + jsonSchema: {}, + ...annotations +})); +/** +* @category schema id +* @since 3.10.0 +*/ +var GreaterThanSchemaId = GreaterThanSchemaId$1; +/** +* This filter checks whether the provided number is greater than the specified minimum. +* +* @category number filters +* @since 3.10.0 +*/ +var greaterThan = (exclusiveMinimum, annotations) => (self) => self.pipe(filter((a) => a > exclusiveMinimum, { + schemaId: GreaterThanSchemaId, + title: `greaterThan(${exclusiveMinimum})`, + description: exclusiveMinimum === 0 ? "a positive number" : `a number greater than ${exclusiveMinimum}`, + jsonSchema: { exclusiveMinimum }, + ...annotations +})); +/** +* @category schema id +* @since 3.10.0 +*/ +var GreaterThanOrEqualToSchemaId = GreaterThanOrEqualToSchemaId$1; +/** +* This filter checks whether the provided number is greater than or equal to the specified minimum. +* +* @category number filters +* @since 3.10.0 +*/ +var greaterThanOrEqualTo = (minimum, annotations) => (self) => self.pipe(filter((a) => a >= minimum, { + schemaId: GreaterThanOrEqualToSchemaId, + title: `greaterThanOrEqualTo(${minimum})`, + description: minimum === 0 ? "a non-negative number" : `a number greater than or equal to ${minimum}`, + jsonSchema: { minimum }, + ...annotations +})); +/** +* @category schema id +* @since 3.10.0 +*/ +var IntSchemaId = IntSchemaId$1; +/** +* Ensures that the provided value is an integer number (excluding NaN, +Infinity, and -Infinity). +* +* @category number filters +* @since 3.10.0 +*/ +var int = (annotations) => (self) => self.pipe(filter((a) => Number.isSafeInteger(a), { + schemaId: IntSchemaId, + title: "int", + description: "an integer", + jsonSchema: { type: "integer" }, + ...annotations +})); +/** +* @category schema id +* @since 3.10.0 +*/ +var LessThanSchemaId = LessThanSchemaId$1; +/** +* This filter checks whether the provided number is less than the specified maximum. +* +* @category number filters +* @since 3.10.0 +*/ +var lessThan = (exclusiveMaximum, annotations) => (self) => self.pipe(filter((a) => a < exclusiveMaximum, { + schemaId: LessThanSchemaId, + title: `lessThan(${exclusiveMaximum})`, + description: exclusiveMaximum === 0 ? "a negative number" : `a number less than ${exclusiveMaximum}`, + jsonSchema: { exclusiveMaximum }, + ...annotations +})); +/** +* @category schema id +* @since 3.10.0 +*/ +var LessThanOrEqualToSchemaId = LessThanOrEqualToSchemaId$1; +/** +* This schema checks whether the provided number is less than or equal to the specified maximum. +* +* @category number filters +* @since 3.10.0 +*/ +var lessThanOrEqualTo = (maximum, annotations) => (self) => self.pipe(filter((a) => a <= maximum, { + schemaId: LessThanOrEqualToSchemaId, + title: `lessThanOrEqualTo(${maximum})`, + description: maximum === 0 ? "a non-positive number" : `a number less than or equal to ${maximum}`, + jsonSchema: { maximum }, + ...annotations +})); +/** +* @category schema id +* @since 3.10.0 +*/ +var BetweenSchemaId = BetweenSchemaId$1; +/** +* This filter checks whether the provided number falls within the specified minimum and maximum values. +* +* @category number filters +* @since 3.10.0 +*/ +var between = (minimum, maximum, annotations) => (self) => self.pipe(filter((a) => a >= minimum && a <= maximum, { + schemaId: BetweenSchemaId, + title: `between(${minimum}, ${maximum})`, + description: `a number between ${minimum} and ${maximum}`, + jsonSchema: { + minimum, + maximum + }, + ...annotations +})); +/** +* @category schema id +* @since 3.10.0 +*/ +var NonNaNSchemaId = NonNaNSchemaId$1; +/** +* @category number filters +* @since 3.10.0 +*/ +var nonNaN = (annotations) => (self) => self.pipe(filter((a) => !Number.isNaN(a), { + schemaId: NonNaNSchemaId, + title: "nonNaN", + description: "a number excluding NaN", + ...annotations +})); +/** +* @category number filters +* @since 3.10.0 +*/ +var positive = (annotations) => greaterThan(0, { + title: "positive", + ...annotations +}); +/** +* @category number filters +* @since 3.10.0 +*/ +var negative = (annotations) => lessThan(0, { + title: "negative", + ...annotations +}); +/** +* @category number filters +* @since 3.10.0 +*/ +var nonPositive = (annotations) => lessThanOrEqualTo(0, { + title: "nonPositive", + ...annotations +}); +/** +* @category number filters +* @since 3.10.0 +*/ +var nonNegative = (annotations) => greaterThanOrEqualTo(0, { + title: "nonNegative", + ...annotations +}); +/** +* Transforms a `string` into a `number` by parsing the string using the `parse` +* function of the `effect/Number` module. +* +* It returns an error if the value can't be converted (for example when +* non-numeric characters are provided). +* +* The following special string values are supported: "NaN", "Infinity", +* "-Infinity". +* +* @category number transformations +* @since 3.10.0 +*/ +function parseNumber(self) { + return transformOrFail(self, Number$, { + strict: false, + decode: (i, _, ast) => fromOption(require_Layer.parse(i), () => new Type(ast, i, `Unable to decode ${JSON.stringify(i)} into a number`)), + encode: (a) => succeed(String(a)) + }); +} +parseNumber(String$.annotations({ description: "a string to be decoded into a number" })).annotations({ identifier: "NumberFromString" }); +Number$.pipe(/* @__PURE__ */ finite({ identifier: "Finite" })); +/** +* @category number constructors +* @since 3.10.0 +*/ +var Int = class extends Number$.pipe(/* @__PURE__ */ int({ identifier: "Int" })) {}; +Number$.pipe(/* @__PURE__ */ nonNaN({ identifier: "NonNaN" })); +Number$.pipe(/* @__PURE__ */ positive({ identifier: "Positive" })); +Number$.pipe(/* @__PURE__ */ negative({ identifier: "Negative" })); +Number$.pipe(/* @__PURE__ */ nonPositive({ identifier: "NonPositive" })); +/** +* @category number constructors +* @since 3.10.0 +*/ +var NonNegative = class extends Number$.pipe(/* @__PURE__ */ nonNegative({ identifier: "NonNegative" })) {}; +/** +* @category schema id +* @since 3.10.0 +*/ +var JsonNumberSchemaId = JsonNumberSchemaId$1; +Number$.pipe(/* @__PURE__ */ finite({ + schemaId: JsonNumberSchemaId, + identifier: "JsonNumber" +})); +transform(/* @__PURE__ */ Boolean$.annotations({ description: "a boolean that will be negated" }), Boolean$, { + strict: true, + decode: (i) => require_Layer.not(i), + encode: (a) => require_Layer.not(a) +}); +var encodeSymbol = (sym, ast) => { + const key = Symbol.keyFor(sym); + return key === void 0 ? fail(new Type(ast, sym, `Unable to encode a unique symbol ${String(sym)} into a string`)) : succeed(key); +}; +var decodeSymbol = (s) => succeed(Symbol.for(s)); +transformOrFail(String$.annotations({ description: "a string to be decoded into a globally shared symbol" }), SymbolFromSelf, { + strict: false, + decode: (i) => decodeSymbol(i), + encode: (a, _, ast) => encodeSymbol(a, ast) +}).annotations({ identifier: "Symbol" }); +/** +* @category schema id +* @since 3.10.0 +*/ +var GreaterThanOrEqualToBigIntSchemaId = GreaterThanOrEqualToBigIntSchemaId$1; +/** +* @category bigint filters +* @since 3.10.0 +*/ +var greaterThanOrEqualToBigInt = (min, annotations) => (self) => self.pipe(filter((a) => a >= min, { + schemaId: GreaterThanOrEqualToBigIntSchemaId, + [GreaterThanOrEqualToBigIntSchemaId]: { min }, + title: `greaterThanOrEqualToBigInt(${min})`, + description: min === 0n ? "a non-negative bigint" : `a bigint greater than or equal to ${min}n`, + ...annotations +})); +/** +* @category schema id +* @since 3.10.0 +*/ +var BetweenBigIntSchemaId = BetweenBigintSchemaId; +/** +* @category bigint filters +* @since 3.10.0 +*/ +var betweenBigInt = (min, max, annotations) => (self) => self.pipe(filter((a) => a >= min && a <= max, { + schemaId: BetweenBigIntSchemaId, + [BetweenBigIntSchemaId]: { + min, + max + }, + title: `betweenBigInt(${min}, ${max})`, + description: `a bigint between ${min}n and ${max}n`, + ...annotations +})); +/** +* @category bigint filters +* @since 3.10.0 +*/ +var nonNegativeBigInt = (annotations) => greaterThanOrEqualToBigInt(0n, { + title: "nonNegativeBigInt", + ...annotations +}); +/** @ignore */ +var BigInt$ = class extends transformOrFail(String$.annotations({ description: "a string to be decoded into a bigint" }), BigIntFromSelf, { + strict: true, + decode: (i, _, ast) => fromOption(fromString(i), () => new Type(ast, i, `Unable to decode ${JSON.stringify(i)} into a bigint`)), + encode: (a) => succeed(String(a)) +}).annotations({ identifier: "BigInt" }) {}; +/** +* @category bigint constructors +* @since 3.10.0 +*/ +var NonNegativeBigIntFromSelf = /* @__PURE__ */ BigIntFromSelf.pipe(/* @__PURE__ */ nonNegativeBigInt({ identifier: "NonNegativeBigintFromSelf" })); +transformOrFail(Number$.annotations({ description: "a number to be decoded into a bigint" }), BigIntFromSelf.pipe(betweenBigInt(BigInt(Number.MIN_SAFE_INTEGER), BigInt(Number.MAX_SAFE_INTEGER))), { + strict: true, + decode: (i, _, ast) => fromOption(fromNumber(i), () => new Type(ast, i, `Unable to decode ${i} into a bigint`)), + encode: (a, _, ast) => fromOption(toNumber(a), () => new Type(ast, a, `Unable to encode ${a}n into a number`)) +}).annotations({ identifier: "BigIntFromNumber" }); +var toComposite = (eff, onSuccess, ast, actual) => mapBoth(eff, { + onFailure: (e) => new Composite(ast, actual, e), + onSuccess +}); +/** +* @category Duration constructors +* @since 3.10.0 +*/ +var DurationFromSelf = class extends declare(require_Layer.isDuration, { + typeConstructor: { _tag: "effect/Duration" }, + identifier: "DurationFromSelf", + pretty: () => String, + arbitrary: () => (fc) => fc.oneof(fc.constant(require_Layer.infinity), fc.bigInt({ min: 0n }).map((_) => require_Layer.nanos(_)), fc.maxSafeNat().map((_) => require_Layer.millis(_))), + equivalence: () => require_Layer.Equivalence +}) {}; +transformOrFail(NonNegativeBigIntFromSelf.annotations({ description: "a bigint to be decoded into a Duration" }), DurationFromSelf.pipe(filter((duration) => require_Layer.isFinite(duration), { description: "a finite duration" })), { + strict: true, + decode: (i) => succeed(require_Layer.nanos(i)), + encode: (a, _, ast) => require_Layer.match(require_Layer.toNanos(a), { + onNone: () => fail(new Type(ast, a, `Unable to encode ${a} into a bigint`)), + onSome: (nanos) => succeed(nanos) + }) +}).annotations({ identifier: "DurationFromNanos" }); +/** +* A non-negative integer. +Infinity is excluded. +* +* @category number constructors +* @since 3.11.10 +*/ +var NonNegativeInt = /* @__PURE__ */ NonNegative.pipe(int()).annotations({ identifier: "NonNegativeInt" }); +transform(NonNegative.annotations({ description: "a non-negative number to be decoded into a Duration" }), DurationFromSelf, { + strict: true, + decode: (i) => require_Layer.millis(i), + encode: (a) => require_Layer.toMillis(a) +}).annotations({ identifier: "DurationFromMillis" }); +var DurationValueMillis = /* @__PURE__ */ TaggedStruct("Millis", { millis: NonNegativeInt }); +var DurationValueNanos = /* @__PURE__ */ TaggedStruct("Nanos", { nanos: BigInt$ }); +var DurationValueInfinity = /* @__PURE__ */ TaggedStruct("Infinity", {}); +var durationValueInfinity = /* @__PURE__ */ DurationValueInfinity.make({}); +var DurationValue = /* @__PURE__ */ Union(DurationValueMillis, DurationValueNanos, DurationValueInfinity).annotations({ + identifier: "DurationValue", + description: "an JSON-compatible tagged union to be decoded into a Duration" +}); +var HRTime = /* @__PURE__ */ Union(/* @__PURE__ */ Tuple(element(NonNegativeInt).annotations({ title: "seconds" }), element(NonNegativeInt).annotations({ title: "nanos" })).annotations({ identifier: "FiniteHRTime" }), /* @__PURE__ */ Tuple(Literal(-1), Literal(0)).annotations({ identifier: "InfiniteHRTime" })).annotations({ + identifier: "HRTime", + description: "a tuple of seconds and nanos to be decoded into a Duration" +}); +var isDurationValue = (u) => typeof u === "object"; +transform(Union(DurationValue, HRTime), DurationFromSelf, { + strict: true, + decode: (i) => { + if (isDurationValue(i)) switch (i._tag) { + case "Millis": return require_Layer.millis(i.millis); + case "Nanos": return require_Layer.nanos(i.nanos); + case "Infinity": return require_Layer.infinity; + } + const [seconds, nanos$1] = i; + return seconds === -1 ? require_Layer.infinity : require_Layer.nanos(BigInt(seconds) * BigInt(1e9) + BigInt(nanos$1)); + }, + encode: (a) => { + switch (a.value._tag) { + case "Millis": return DurationValueMillis.make({ millis: a.value.millis }); + case "Nanos": return DurationValueNanos.make({ nanos: a.value.nanos }); + case "Infinity": return durationValueInfinity; + } + } +}).annotations({ identifier: "Duration" }); +/** +* @category Uint8Array constructors +* @since 3.10.0 +*/ +var Uint8ArrayFromSelf = class extends declare(require_Layer.isUint8Array, { + typeConstructor: { _tag: "Uint8Array" }, + identifier: "Uint8ArrayFromSelf", + pretty: () => (u8arr) => `new Uint8Array(${JSON.stringify(Array.from(u8arr))})`, + arbitrary: () => (fc) => fc.uint8Array(), + equivalence: () => require_Layer.getEquivalence(require_Layer.equals) +}) {}; +/** +* @category number constructors +* @since 3.11.10 +*/ +var Uint8 = class extends Number$.pipe(/* @__PURE__ */ between(0, 255, { + identifier: "Uint8", + description: "a 8-bit unsigned integer" +})) {}; +transform(Array$(Uint8).annotations({ description: "an array of 8-bit unsigned integers to be decoded into a Uint8Array" }), Uint8ArrayFromSelf, { + strict: true, + decode: (i) => Uint8Array.from(i), + encode: (a) => Array.from(a) +}).annotations({ identifier: "Uint8Array" }); +/** +* @category schema id +* @since 3.10.0 +*/ +var ValidDateSchemaId = /* @__PURE__ */ Symbol.for("effect/SchemaId/ValidDate"); +/** +* Defines a filter that specifically rejects invalid dates, such as `new +* Date("Invalid Date")`. This filter ensures that only properly formatted and +* valid date objects are accepted, enhancing data integrity by preventing +* erroneous date values from being processed. +* +* @category Date filters +* @since 3.10.0 +*/ +var validDate = (annotations) => (self) => self.pipe(filter((a) => !Number.isNaN(a.getTime()), { + schemaId: ValidDateSchemaId, + [ValidDateSchemaId]: { noInvalidDate: true }, + title: "validDate", + description: "a valid Date", + ...annotations +})); +/** +* @category schema id +* @since 3.11.8 +*/ +var DateFromSelfSchemaId = DateFromSelfSchemaId$1; +/** +* Describes a schema that accommodates potentially invalid `Date` instances, +* such as `new Date("Invalid Date")`, without rejection. +* +* @category Date constructors +* @since 3.10.0 +*/ +var DateFromSelf = class extends declare(require_Layer.isDate, { + typeConstructor: { _tag: "Date" }, + identifier: "DateFromSelf", + schemaId: DateFromSelfSchemaId, + [DateFromSelfSchemaId]: { noInvalidDate: false }, + description: "a potentially invalid Date instance", + pretty: () => (date) => `new Date(${JSON.stringify(date)})`, + arbitrary: () => (fc) => fc.date({ noInvalidDate: false }), + equivalence: () => require_Layer.Date +}) {}; +DateFromSelf.pipe(/* @__PURE__ */ validDate({ + identifier: "ValidDateFromSelf", + description: "a valid Date instance" +})); +/** +* Defines a schema that attempts to convert a `string` to a `Date` object using +* the `new Date` constructor. This conversion is lenient, meaning it does not +* reject strings that do not form valid dates (e.g., using `new Date("Invalid +* Date")` results in a `Date` object, despite being invalid). +* +* @category Date transformations +* @since 3.10.0 +*/ +var DateFromString = class extends transform(String$.annotations({ description: "a string to be decoded into a Date" }), DateFromSelf, { + strict: true, + decode: (i) => new Date(i), + encode: (a) => require_Layer.formatDate(a) +}).annotations({ identifier: "DateFromString" }) {}; +DateFromString.pipe(/* @__PURE__ */ validDate({ identifier: "Date" })); +transform(Number$.annotations({ description: "a number to be decoded into a Date" }), DateFromSelf, { + strict: true, + decode: (i) => new Date(i), + encode: (a) => a.getTime() +}).annotations({ identifier: "DateFromNumber" }); +/** +* Describes a schema that represents a `DateTime.Utc` instance. +* +* @category DateTime.Utc constructors +* @since 3.10.0 +*/ +var DateTimeUtcFromSelf = class extends declare((u) => isDateTime(u) && isUtc(u), { + typeConstructor: { _tag: "effect/DateTime.Utc" }, + identifier: "DateTimeUtcFromSelf", + description: "a DateTime.Utc instance", + pretty: () => (dateTime) => dateTime.toString(), + arbitrary: () => (fc) => fc.date({ noInvalidDate: true }).map((date) => unsafeFromDate(date)), + equivalence: () => Equivalence +}) {}; +var decodeDateTimeUtc = (input, ast) => _try({ + try: () => unsafeMake(input), + catch: () => new Type(ast, input, `Unable to decode ${require_Layer.formatUnknown(input)} into a DateTime.Utc`) +}); +transformOrFail(Number$.annotations({ description: "a number to be decoded into a DateTime.Utc" }), DateTimeUtcFromSelf, { + strict: true, + decode: (i, _, ast) => decodeDateTimeUtc(i, ast), + encode: (a) => succeed(toEpochMillis(a)) +}).annotations({ identifier: "DateTimeUtcFromNumber" }); +transformOrFail(DateFromSelf.annotations({ description: "a Date to be decoded into a DateTime.Utc" }), DateTimeUtcFromSelf, { + strict: true, + decode: (i, _, ast) => decodeDateTimeUtc(i, ast), + encode: (a) => succeed(toDateUtc(a)) +}).annotations({ identifier: "DateTimeUtcFromDate" }); +transformOrFail(String$.annotations({ description: "a string to be decoded into a DateTime.Utc" }), DateTimeUtcFromSelf, { + strict: true, + decode: (i, _, ast) => decodeDateTimeUtc(i, ast), + encode: (a) => succeed(formatIso(a)) +}).annotations({ identifier: "DateTimeUtc" }); +var timeZoneOffsetArbitrary = () => (fc) => fc.integer({ + min: -720 * 60 * 1e3, + max: 840 * 60 * 1e3 +}).map(zoneMakeOffset); +/** +* Describes a schema that represents a `TimeZone.Offset` instance. +* +* @category TimeZone constructors +* @since 3.10.0 +*/ +var TimeZoneOffsetFromSelf = class extends declare(isTimeZoneOffset, { + typeConstructor: { _tag: "effect/DateTime.TimeZone.Offset" }, + identifier: "TimeZoneOffsetFromSelf", + description: "a TimeZone.Offset instance", + pretty: () => (zone) => zone.toString(), + arbitrary: timeZoneOffsetArbitrary +}) {}; +transform(Number$.annotations({ description: "a number to be decoded into a TimeZone.Offset" }), TimeZoneOffsetFromSelf, { + strict: true, + decode: (i) => zoneMakeOffset(i), + encode: (a) => a.offset +}).annotations({ identifier: "TimeZoneOffset" }); +var timeZoneNamedArbitrary = () => (fc) => fc.constantFrom(...Intl.supportedValuesOf("timeZone")).map(zoneUnsafeMakeNamed); +/** +* Describes a schema that represents a `TimeZone.Named` instance. +* +* @category TimeZone constructors +* @since 3.10.0 +*/ +var TimeZoneNamedFromSelf = class extends declare(isTimeZoneNamed, { + typeConstructor: { _tag: "effect/DateTime.TimeZone.Named" }, + identifier: "TimeZoneNamedFromSelf", + description: "a TimeZone.Named instance", + pretty: () => (zone) => zone.toString(), + arbitrary: timeZoneNamedArbitrary +}) {}; +transformOrFail(String$.annotations({ description: "a string to be decoded into a TimeZone.Named" }), TimeZoneNamedFromSelf, { + strict: true, + decode: (i, _, ast) => _try({ + try: () => zoneUnsafeMakeNamed(i), + catch: () => new Type(ast, i, `Unable to decode ${JSON.stringify(i)} into a TimeZone.Named`) + }), + encode: (a) => succeed(a.id) +}).annotations({ identifier: "TimeZoneNamed" }); +/** +* @category TimeZone constructors +* @since 3.10.0 +*/ +var TimeZoneFromSelf = class extends Union(TimeZoneOffsetFromSelf, TimeZoneNamedFromSelf) {}; +transformOrFail(String$.annotations({ description: "a string to be decoded into a TimeZone" }), TimeZoneFromSelf, { + strict: true, + decode: (i, _, ast) => require_Layer.match(zoneFromString(i), { + onNone: () => fail(new Type(ast, i, `Unable to decode ${JSON.stringify(i)} into a TimeZone`)), + onSome: succeed + }), + encode: (a) => succeed(zoneToString(a)) +}).annotations({ identifier: "TimeZone" }); +var timeZoneArbitrary = (fc) => fc.oneof(timeZoneOffsetArbitrary()(fc), timeZoneNamedArbitrary()(fc)); +/** +* Describes a schema that represents a `DateTime.Zoned` instance. +* +* @category DateTime.Zoned constructors +* @since 3.10.0 +*/ +var DateTimeZonedFromSelf = class extends declare((u) => isDateTime(u) && isZoned(u), { + typeConstructor: { _tag: "effect/DateTime.Zoned" }, + identifier: "DateTimeZonedFromSelf", + description: "a DateTime.Zoned instance", + pretty: () => (dateTime) => dateTime.toString(), + arbitrary: () => (fc) => fc.tuple(fc.integer({ + min: -31536e9, + max: 31536e9 + }), timeZoneArbitrary(fc)).map(([millis, timeZone]) => unsafeMakeZoned(millis, { timeZone })), + equivalence: () => Equivalence +}) {}; +transformOrFail(String$.annotations({ description: "a string to be decoded into a DateTime.Zoned" }), DateTimeZonedFromSelf, { + strict: true, + decode: (i, _, ast) => require_Layer.match(makeZonedFromString(i), { + onNone: () => fail(new Type(ast, i, `Unable to decode ${JSON.stringify(i)} into a DateTime.Zoned`)), + onSome: succeed + }), + encode: (a) => succeed(formatIsoZoned(a)) +}).annotations({ identifier: "DateTimeZoned" }); +var optionDecode = (input) => input._tag === "None" ? require_Layer.none$1() : require_Layer.some(input.value); +var optionArbitrary = (value, ctx) => (fc) => fc.oneof(ctx, fc.record({ _tag: fc.constant("None") }), fc.record({ + _tag: fc.constant("Some"), + value: value(fc) +})).map(optionDecode); +var optionPretty = (value) => require_Layer.match({ + onNone: () => "none()", + onSome: (a) => `some(${value(a)})` +}); +var optionParse = (decodeUnknown) => (u, options, ast) => require_Layer.isOption(u) ? require_Layer.isNone(u) ? succeed(require_Layer.none$1()) : toComposite(decodeUnknown(u.value, options), require_Layer.some, ast, u) : fail(new Type(ast, u)); +var OptionFromSelf_ = (value) => { + return declare([value], { + decode: (value) => optionParse(decodeUnknown(value)), + encode: (value) => optionParse(encodeUnknown(value)) + }, { + typeConstructor: { _tag: "effect/Option" }, + pretty: optionPretty, + arbitrary: optionArbitrary, + equivalence: require_Layer.getEquivalence$1 + }); +}; +/** +* @category Option transformations +* @since 3.10.0 +*/ +var OptionFromSelf = (value) => { + return OptionFromSelf_(value).annotations({ description: `Option<${format(value)}>` }); +}; +transform(String$, /* @__PURE__ */ OptionFromSelf(NonEmptyTrimmedString), { + strict: true, + decode: (i) => require_Layer.filter(require_Layer.some(i.trim()), isNonEmpty), + encode: (a) => require_Layer.getOrElse(a, () => "") +}); +var bigDecimalPretty = () => (val) => `BigDecimal(${format$1(normalize(val))})`; +var bigDecimalArbitrary = () => (fc) => fc.tuple(fc.bigInt(), fc.integer({ + min: -18, + max: 18 +})).map(([value, scale]) => make$1(value, scale)); +/** +* @category BigDecimal constructors +* @since 3.10.0 +*/ +var BigDecimalFromSelf = class extends declare(isBigDecimal, { + typeConstructor: { _tag: "effect/BigDecimal" }, + identifier: "BigDecimalFromSelf", + pretty: bigDecimalPretty, + arbitrary: bigDecimalArbitrary, + equivalence: () => Equivalence$2 +}) {}; +transformOrFail(String$.annotations({ description: "a string to be decoded into a BigDecimal" }), BigDecimalFromSelf, { + strict: true, + decode: (i, _, ast) => fromString$1(i).pipe(require_Layer.match({ + onNone: () => fail(new Type(ast, i, `Unable to decode ${JSON.stringify(i)} into a BigDecimal`)), + onSome: (val) => succeed(normalize(val)) + })), + encode: (a) => succeed(format$1(normalize(a))) +}).annotations({ identifier: "BigDecimal" }); +transform(Number$.annotations({ description: "a number to be decoded into a BigDecimal" }), BigDecimalFromSelf, { + strict: true, + decode: (i) => unsafeFromNumber(i), + encode: (a) => unsafeToNumber(a) +}).annotations({ identifier: "BigDecimalFromNumber" }); +function getDisableValidationMakeOption(options) { + return require_Layer.isBoolean(options) ? options : options?.disableValidation ?? false; +} +var FiberIdEncoded = /* @__PURE__ */ Union(/* @__PURE__ */ Struct({ _tag: Literal("None") }).annotations({ identifier: "FiberIdNoneEncoded" }), /* @__PURE__ */ Struct({ + _tag: Literal("Runtime"), + id: Int, + startTimeMillis: Int +}).annotations({ identifier: "FiberIdRuntimeEncoded" }), /* @__PURE__ */ Struct({ + _tag: Literal("Composite"), + left: suspend(() => FiberIdEncoded), + right: suspend(() => FiberIdEncoded) +}).annotations({ identifier: "FiberIdCompositeEncoded" })).annotations({ identifier: "FiberIdEncoded" }); +var fiberIdArbitrary = (fc) => fc.letrec((tie) => ({ + None: fc.record({ _tag: fc.constant("None") }), + Runtime: fc.record({ + _tag: fc.constant("Runtime"), + id: fc.integer(), + startTimeMillis: fc.integer() + }), + Composite: fc.record({ + _tag: fc.constant("Composite"), + left: tie("FiberId"), + right: tie("FiberId") + }), + FiberId: fc.oneof(tie("None"), tie("Runtime"), tie("Composite")) +})).FiberId.map(fiberIdDecode); +var fiberIdPretty = (fiberId) => { + switch (fiberId._tag) { + case "None": return "FiberId.none"; + case "Runtime": return `FiberId.runtime(${fiberId.id}, ${fiberId.startTimeMillis})`; + case "Composite": return `FiberId.composite(${fiberIdPretty(fiberId.right)}, ${fiberIdPretty(fiberId.left)})`; + } +}; +/** +* @category FiberId constructors +* @since 3.10.0 +*/ +var FiberIdFromSelf = class extends declare(require_Layer.isFiberId, { + typeConstructor: { _tag: "effect/FiberId" }, + identifier: "FiberIdFromSelf", + pretty: () => fiberIdPretty, + arbitrary: () => fiberIdArbitrary +}) {}; +var fiberIdDecode = (input) => { + switch (input._tag) { + case "None": return require_Layer.none; + case "Runtime": return require_Layer.runtime(input.id, input.startTimeMillis); + case "Composite": return require_Layer.composite(fiberIdDecode(input.left), fiberIdDecode(input.right)); + } +}; +var fiberIdEncode = (input) => { + switch (input._tag) { + case "None": return { _tag: "None" }; + case "Runtime": return { + _tag: "Runtime", + id: input.id, + startTimeMillis: input.startTimeMillis + }; + case "Composite": return { + _tag: "Composite", + left: fiberIdEncode(input.left), + right: fiberIdEncode(input.right) + }; + } +}; +transform(FiberIdEncoded, FiberIdFromSelf, { + strict: true, + decode: (i) => fiberIdDecode(i), + encode: (a) => fiberIdEncode(a) +}).annotations({ identifier: "FiberId" }); +transform(Unknown, Unknown, { + strict: true, + decode: (i) => { + if (require_Layer.isObject(i) && "message" in i && typeof i.message === "string") { + const err = new Error(i.message, { cause: i }); + if ("name" in i && typeof i.name === "string") err.name = i.name; + err.stack = "stack" in i && typeof i.stack === "string" ? i.stack : ""; + return err; + } + return require_Layer.prettyErrorMessage(i); + }, + encode: (a) => { + if (a instanceof Error) return { + name: a.name, + message: a.message + }; + return require_Layer.prettyErrorMessage(a); + } +}).annotations({ identifier: "Defect" }); +transform(Unknown, Boolean$, { + strict: true, + decode: (i) => require_Layer.isTruthy(i), + encode: require_Layer.identity +}).annotations({ identifier: "BooleanFromUnknown" }); +transform(Literal("true", "false").annotations({ description: "a string to be decoded into a boolean" }), Boolean$, { + strict: true, + decode: (i) => i === "true", + encode: (a) => a ? "true" : "false" +}).annotations({ identifier: "BooleanFromString" }); +var SymbolStruct = /* @__PURE__ */ TaggedStruct("symbol", { key: String$ }).annotations({ description: "an object to be decoded into a globally shared symbol" }); +var SymbolFromStruct = /* @__PURE__ */ transformOrFail(SymbolStruct, SymbolFromSelf, { + strict: true, + decode: (i) => decodeSymbol(i.key), + encode: (a, _, ast) => map(encodeSymbol(a, ast), (key) => SymbolStruct.make({ key })) +}); +/** @ignore */ +var PropertyKey$ = class extends Union(String$, Number$, SymbolFromStruct).annotations({ identifier: "PropertyKey" }) {}; +Struct({ + _tag: propertySignature(Literal("Pointer", "Unexpected", "Missing", "Composite", "Refinement", "Transformation", "Type", "Forbidden")).annotations({ description: "The tag identifying the type of parse issue" }), + path: propertySignature(Array$(PropertyKey$)).annotations({ description: "The path to the property where the issue occurred" }), + message: propertySignature(String$).annotations({ description: "A descriptive message explaining the issue" }) +}).annotations({ + identifier: "ArrayFormatterIssue", + description: "Represents an issue returned by the ArrayFormatter formatter" +}); +//#endregion +//#region src/shared/rpc.ts +var JsonRpcErrorSchema = Struct({ + code: Number$, + message: String$ +}); +var JsonRpcMessageSchema = Union(Struct({ + jsonrpc: Literal("2.0"), + id: optional(Union(Number$, String$, Null)), + method: String$, + params: optional(Unknown) +}), Struct({ + jsonrpc: Literal("2.0"), + id: Union(Number$, String$, Null), + result: optional(Unknown), + error: optional(JsonRpcErrorSchema) +})); +function parseJsonRpcMessage(value) { + const decoded = decodeUnknownEither(JsonRpcMessageSchema)(value); + if (require_Layer.isLeft(decoded)) throw new Error("Invalid JSON-RPC 2.0 message"); + return decoded.right; +} +function isJsonRpcRequest(message) { + return "method" in message; +} +//#endregion +Object.defineProperty(exports, "isJsonRpcRequest", { + enumerable: true, + get: function() { + return isJsonRpcRequest; + } +}); +Object.defineProperty(exports, "parseJsonRpcMessage", { + enumerable: true, + get: function() { + return parseJsonRpcMessage; + } +}); diff --git a/skills/chrome-control/native-host/client.js b/skills/chrome-control/native-host/client.js index 774ccbe..4f1fee5 100644 --- a/skills/chrome-control/native-host/client.js +++ b/skills/chrome-control/native-host/client.js @@ -1,75 +1,168 @@ #!/usr/bin/env node -const require_NodeRuntime = require("../chunks/NodeRuntime-CfddDOOs.js"); +const require_Layer = require("../chunks/Layer-nAKmzBoW.js"); +const require_rpc = require("../chunks/rpc-BTiTvO3J.js"); +let node_fs = require("node:fs"); +node_fs = require_Layer.__toESM(node_fs); let node_net = require("node:net"); -node_net = require_NodeRuntime.__toESM(node_net); +node_net = require_Layer.__toESM(node_net); let node_os = require("node:os"); -node_os = require_NodeRuntime.__toESM(node_os); +node_os = require_Layer.__toESM(node_os); let node_path = require("node:path"); -node_path = require_NodeRuntime.__toESM(node_path); +node_path = require_Layer.__toESM(node_path); let node_process = require("node:process"); -node_process = require_NodeRuntime.__toESM(node_process); +node_process = require_Layer.__toESM(node_process); //#region src/native-host/client.ts -var ClientIo = class extends require_NodeRuntime.Tag("opzero/NativeHostClientIo")() {}; -function toError(error) { - return error instanceof Error ? error : new Error(String(error || "Unknown error")); +var args = node_process.default.argv.slice(2); +if (args[0] === "--") args.shift(); +var streaming = args[0] === "--stdio"; +var method = args[0] || "ping"; +if (args.length > 1 || !streaming && ![ + "ping", + "getInfo", + "host.ping", + "host.info" +].includes(method)) { + node_process.default.stderr.write("Payloads are accepted only through --stdio JSONL; never pass private values in argv\n"); + node_process.default.exit(1); } -function parseArgs(argv) { - const args = argv.slice(2); - if (args[0] === "--") args.shift(); - return { - method: args[0] || "ping", - params: args[1] ? JSON.parse(args[1]) : {} - }; +var useTcp = node_process.default.platform === "win32" || node_process.default.env.OPZERO_CHROME_HOST_TRANSPORT === "tcp"; +var socket = useTcp ? node_net.default.connect(Number(node_process.default.env.OPZERO_CHROME_HOST_PORT || 17365), "127.0.0.1") : node_net.default.connect(node_process.default.env.OPZERO_CHROME_HOST_SOCKET || node_path.default.join(node_os.default.homedir(), ".opzero-chrome", "default.sock")); +var pending = /* @__PURE__ */ new Map(); +var ready = false; +var inputEnded = false; +var input = ""; +var output = ""; +var stopped = false; +var maxBytes = 1024 * 1024; +var maxResponseBytes = 64 * maxBytes; +function fail() { + if (stopped) return; + stopped = true; + node_process.default.stderr.write("Private client stopped; outcome may be unknown; do not replay\n"); + socket.destroy(); + node_process.default.exitCode = 1; + node_process.default.stdin.destroy(); } -function requestNativeHost(config) { - return new Promise((resolve, reject) => { - const socket = config.useTcp ? node_net.default.connect(config.port, "127.0.0.1") : node_net.default.connect(config.socketPath); - socket.setEncoding("utf8"); - socket.on("connect", () => { - socket.write(`${JSON.stringify({ - jsonrpc: "2.0", - id: 1, - method: config.method, - params: config.params - })}\n`); - }); - socket.on("data", (chunk) => { - resolve(String(chunk)); - socket.end(); - }); - socket.on("error", reject); - }); +function finish() { + socket.setTimeout(pending.size ? 35e3 : 0); + if (inputEnded && pending.size === 0) socket.end(); +} +function drain() { + if (!ready) return; + let index; + while ((index = input.indexOf("\n")) !== -1) { + const line = input.slice(0, index); + input = input.slice(index + 1); + if (!line.trim()) continue; + try { + const request = require_rpc.parseJsonRpcMessage(JSON.parse(line)); + if (!require_rpc.isJsonRpcRequest(request) || request.id == null || request.id === "__auth" || pending.has(request.id)) throw new Error(); + pending.set(request.id, request.method); + socket.write(`${JSON.stringify(request)}\n`); + } catch { + fail(); + return; + } + } + finish(); } -var ClientIoLive = require_NodeRuntime.succeed(ClientIo, { - readConfig: () => require_NodeRuntime.try_({ - try: () => { - const { method, params } = parseArgs(node_process.default.argv); - return { - method, - params, - useTcp: node_process.default.platform === "win32" || node_process.default.env.OPZERO_CHROME_HOST_TRANSPORT === "tcp", - port: Number(node_process.default.env.OPZERO_CHROME_HOST_PORT || 17365), - socketPath: node_process.default.env.OPZERO_CHROME_HOST_SOCKET || node_path.default.join(node_os.default.tmpdir(), "opzero-chrome-native-host.sock") - }; - }, - catch: toError - }), - request: (config) => require_NodeRuntime.tryPromise({ - try: () => requestNativeHost(config), - catch: toError - }), - writeStdout: (text) => require_NodeRuntime.sync(() => { - node_process.default.stdout.write(text); - }), - writeStderr: (text) => require_NodeRuntime.sync(() => { - node_process.default.stderr.write(text); - }) +function start() { + ready = true; + if (!streaming) { + input = `${JSON.stringify({ + jsonrpc: "2.0", + id: 1, + method, + params: {} + })}\n`; + inputEnded = true; + } + drain(); +} +socket.setEncoding("utf8"); +socket.setTimeout(35e3); +socket.on("timeout", fail); +socket.on("connect", () => { + if (!useTcp) { + start(); + return; + } + try { + const file = node_process.default.env.OPZERO_CHROME_HOST_TOKEN_FILE; + if (!file) throw new Error(); + socket.write(`${JSON.stringify({ + jsonrpc: "2.0", + id: "__auth", + method: "host.authenticate", + params: { token: node_fs.default.readFileSync(file, "utf8").trim() } + })}\n`); + } catch { + fail(); + } +}); +socket.on("data", (chunk) => { + output += chunk; + if (Buffer.byteLength(output) > maxResponseBytes) { + fail(); + return; + } + let index; + while ((index = output.indexOf("\n")) !== -1) { + const line = output.slice(0, index); + output = output.slice(index + 1); + try { + const response = require_rpc.parseJsonRpcMessage(JSON.parse(line)); + if (require_rpc.isJsonRpcRequest(response)) continue; + if (response.id === "__auth") { + if (response.error) fail(); + else start(); + continue; + } + if (response.id == null || !pending.has(response.id)) continue; + const requestMethod = pending.get(response.id); + pending.delete(response.id); + if (requestMethod === "privateFill") { + const value = response.result; + const filled = !response.error && typeof value === "object" && value !== null && "status" in value && value.status === "filled"; + const unsupported = !response.error && typeof value === "object" && value !== null && "status" in value && value.status === "unsupported"; + const notReady = !response.error && typeof value === "object" && value !== null && "status" in value && value.status === "not-ready"; + node_process.default.stdout.write(`${JSON.stringify({ + jsonrpc: "2.0", + id: response.id, + result: { + status: filled ? "filled" : unsupported ? "unsupported" : notReady ? "not-ready" : "not-filled-or-unknown", + retry: false + } + })}\n`); + } else node_process.default.stdout.write(`${JSON.stringify(response)}\n`); + if (response.error) node_process.default.exitCode = 1; + } catch { + fail(); + return; + } + } + finish(); }); -var program = require_NodeRuntime.gen(function* () { - const io = yield* ClientIo; - const config = yield* io.readConfig(); - const response = yield* io.request(config); - yield* io.writeStdout(response); +socket.on("error", fail); +socket.on("close", () => { + if (pending.size || output) fail(); + node_process.default.stdin.destroy(); }); -require_NodeRuntime.runMain(require_NodeRuntime.provide(program, ClientIoLive)); +if (streaming) { + node_process.default.stdin.setEncoding("utf8"); + node_process.default.stdin.on("data", (chunk) => { + input += chunk; + if (Buffer.byteLength(input) > maxBytes) { + fail(); + return; + } + drain(); + }); + node_process.default.stdin.on("end", () => { + inputEnded = true; + if (input.trim()) input += "\n"; + drain(); + }); + node_process.default.stdin.on("error", fail); +} //#endregion diff --git a/skills/chrome-control/native-host/host.js b/skills/chrome-control/native-host/host.js index f537cb4..98c0c85 100644 --- a/skills/chrome-control/native-host/host.js +++ b/skills/chrome-control/native-host/host.js @@ -1,5584 +1,312 @@ #!/usr/bin/env node -const require_NodeRuntime = require("../chunks/NodeRuntime-CfddDOOs.js"); +const require_Layer = require("../chunks/Layer-nAKmzBoW.js"); +const require_rpc = require("../chunks/rpc-BTiTvO3J.js"); let node_fs = require("node:fs"); -node_fs = require_NodeRuntime.__toESM(node_fs); +node_fs = require_Layer.__toESM(node_fs); let node_net = require("node:net"); -node_net = require_NodeRuntime.__toESM(node_net); +node_net = require_Layer.__toESM(node_net); let node_os = require("node:os"); -node_os = require_NodeRuntime.__toESM(node_os); +node_os = require_Layer.__toESM(node_os); let node_path = require("node:path"); -node_path = require_NodeRuntime.__toESM(node_path); +node_path = require_Layer.__toESM(node_path); let node_process = require("node:process"); -node_process = require_NodeRuntime.__toESM(node_process); -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/schema/util.js -/** @internal */ -var getKeysForIndexSignature = (input, parameter) => { - switch (parameter._tag) { - case "StringKeyword": - case "TemplateLiteral": return Object.keys(input); - case "SymbolKeyword": return Object.getOwnPropertySymbols(input); - case "Refinement": return getKeysForIndexSignature(input, parameter.from); - } -}; -/** @internal */ -var memoizeThunk = (f) => { - let done = false; - let a; - return () => { - if (done) return a; - a = f(); - done = true; - return a; - }; -}; -/** @internal */ -var isNonEmpty$1 = (x) => Array.isArray(x); -/** @internal */ -var isSingle = (x) => !Array.isArray(x); -/** @internal */ -var formatPathKey = (key) => `[${require_NodeRuntime.formatPropertyKey(key)}]`; -/** @internal */ -var formatPath = (path) => isNonEmpty$1(path) ? path.map(formatPathKey).join("") : formatPathKey(path); -//#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/schema/errors.js -var getErrorMessage$1 = (reason, details, path, ast) => { - let out = reason; - if (path && require_NodeRuntime.isNonEmptyReadonlyArray(path)) out += `\nat path: ${formatPath(path)}`; - if (details !== void 0) out += `\ndetails: ${details}`; - if (ast) out += `\nschema (${ast._tag}): ${ast}`; - return out; -}; -/** @internal */ -var getASTUnsupportedKeySchemaErrorMessage = (ast) => getErrorMessage$1("Unsupported key schema", void 0, void 0, ast); -/** @internal */ -var getASTUnsupportedLiteralErrorMessage = (literal) => getErrorMessage$1("Unsupported literal", `literal value: ${require_NodeRuntime.formatUnknown(literal)}`); -/** @internal */ -var getASTDuplicateIndexSignatureErrorMessage = (type) => getErrorMessage$1("Duplicate index signature", `${type} index signature`); -/** @internal */ -var getASTIndexSignatureParameterErrorMessage = /* @__PURE__ */ getErrorMessage$1("Unsupported index signature parameter", "An index signature parameter type must be `string`, `symbol`, a template literal type or a refinement of the previous types"); -/** @internal */ -var getASTRequiredElementFollowinAnOptionalElementErrorMessage = /* @__PURE__ */ getErrorMessage$1("Invalid element", "A required element cannot follow an optional element. ts(1257)"); -/** @internal */ -var getASTDuplicatePropertySignatureTransformationErrorMessage = (key) => getErrorMessage$1("Duplicate property signature transformation", `Duplicate key ${require_NodeRuntime.formatUnknown(key)}`); -/** @internal */ -var getASTDuplicatePropertySignatureErrorMessage = (key) => getErrorMessage$1("Duplicate property signature", `Duplicate key ${require_NodeRuntime.formatUnknown(key)}`); -//#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/schema/schemaId.js -/** @internal */ -var DateFromSelfSchemaId$1 = /* @__PURE__ */ Symbol.for("effect/SchemaId/DateFromSelf"); -/** @internal */ -var GreaterThanSchemaId$1 = /* @__PURE__ */ Symbol.for("effect/SchemaId/GreaterThan"); -/** @internal */ -var GreaterThanOrEqualToSchemaId$1 = /* @__PURE__ */ Symbol.for("effect/SchemaId/GreaterThanOrEqualTo"); -/** @internal */ -var LessThanSchemaId$1 = /* @__PURE__ */ Symbol.for("effect/SchemaId/LessThan"); -/** @internal */ -var LessThanOrEqualToSchemaId$1 = /* @__PURE__ */ Symbol.for("effect/SchemaId/LessThanOrEqualTo"); -/** @internal */ -var IntSchemaId$1 = /* @__PURE__ */ Symbol.for("effect/SchemaId/Int"); -/** @internal */ -var NonNaNSchemaId$1 = /* @__PURE__ */ Symbol.for("effect/SchemaId/NonNaN"); -/** @internal */ -var FiniteSchemaId$1 = /* @__PURE__ */ Symbol.for("effect/SchemaId/Finite"); -/** @internal */ -var JsonNumberSchemaId$1 = /* @__PURE__ */ Symbol.for("effect/SchemaId/JsonNumber"); -/** @internal */ -var BetweenSchemaId$1 = /* @__PURE__ */ Symbol.for("effect/SchemaId/Between"); -/** @internal */ -var GreaterThanOrEqualToBigIntSchemaId$1 = /* @__PURE__ */ Symbol.for("effect/SchemaId/GreaterThanOrEqualToBigint"); -/** @internal */ -var BetweenBigintSchemaId = /* @__PURE__ */ Symbol.for("effect/SchemaId/BetweenBigint"); -/** @internal */ -var MinLengthSchemaId$1 = /* @__PURE__ */ Symbol.for("effect/SchemaId/MinLength"); -/** @internal */ -var LengthSchemaId$1 = /* @__PURE__ */ Symbol.for("effect/SchemaId/Length"); -//#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/SchemaAST.js -/** -* @since 3.10.0 -*/ -/** -* @category annotations -* @since 3.19.0 -* @experimental -*/ -var TypeConstructorAnnotationId = /* @__PURE__ */ Symbol.for("effect/annotation/TypeConstructor"); -/** -* @category annotations -* @since 3.10.0 -*/ -var BrandAnnotationId = /* @__PURE__ */ Symbol.for("effect/annotation/Brand"); -/** -* @category annotations -* @since 3.10.0 -*/ -var SchemaIdAnnotationId = /* @__PURE__ */ Symbol.for("effect/annotation/SchemaId"); -/** -* @category annotations -* @since 3.10.0 -*/ -var MessageAnnotationId = /* @__PURE__ */ Symbol.for("effect/annotation/Message"); -/** -* @category annotations -* @since 3.10.0 -*/ -var MissingMessageAnnotationId = /* @__PURE__ */ Symbol.for("effect/annotation/MissingMessage"); -/** -* @category annotations -* @since 3.10.0 -*/ -var IdentifierAnnotationId = /* @__PURE__ */ Symbol.for("effect/annotation/Identifier"); -/** -* @category annotations -* @since 3.10.0 -*/ -var TitleAnnotationId = /* @__PURE__ */ Symbol.for("effect/annotation/Title"); -/** @internal */ -var AutoTitleAnnotationId = /* @__PURE__ */ Symbol.for("effect/annotation/AutoTitle"); -/** -* @category annotations -* @since 3.10.0 -*/ -var DescriptionAnnotationId = /* @__PURE__ */ Symbol.for("effect/annotation/Description"); -/** -* @category annotations -* @since 3.10.0 -*/ -var ExamplesAnnotationId = /* @__PURE__ */ Symbol.for("effect/annotation/Examples"); -/** -* @category annotations -* @since 3.10.0 -*/ -var DefaultAnnotationId = /* @__PURE__ */ Symbol.for("effect/annotation/Default"); -/** -* @category annotations -* @since 3.10.0 -*/ -var JSONSchemaAnnotationId = /* @__PURE__ */ Symbol.for("effect/annotation/JSONSchema"); -/** -* @category annotations -* @since 3.10.0 -*/ -var ArbitraryAnnotationId = /* @__PURE__ */ Symbol.for("effect/annotation/Arbitrary"); -/** -* @category annotations -* @since 3.10.0 -*/ -var PrettyAnnotationId = /* @__PURE__ */ Symbol.for("effect/annotation/Pretty"); -/** -* @category annotations -* @since 3.10.0 -*/ -var EquivalenceAnnotationId = /* @__PURE__ */ Symbol.for("effect/annotation/Equivalence"); -/** -* @category annotations -* @since 3.10.0 -*/ -var DocumentationAnnotationId = /* @__PURE__ */ Symbol.for("effect/annotation/Documentation"); -/** -* @category annotations -* @since 3.10.0 -*/ -var ConcurrencyAnnotationId = /* @__PURE__ */ Symbol.for("effect/annotation/Concurrency"); -/** -* @category annotations -* @since 3.10.0 -*/ -var BatchingAnnotationId = /* @__PURE__ */ Symbol.for("effect/annotation/Batching"); -/** -* @category annotations -* @since 3.10.0 -*/ -var ParseIssueTitleAnnotationId = /* @__PURE__ */ Symbol.for("effect/annotation/ParseIssueTitle"); -/** -* @category annotations -* @since 3.10.0 -*/ -var ParseOptionsAnnotationId = /* @__PURE__ */ Symbol.for("effect/annotation/ParseOptions"); -/** -* @category annotations -* @since 3.10.0 -*/ -var DecodingFallbackAnnotationId = /* @__PURE__ */ Symbol.for("effect/annotation/DecodingFallback"); -/** -* @category annotations -* @since 3.10.0 -*/ -var SurrogateAnnotationId = /* @__PURE__ */ Symbol.for("effect/annotation/Surrogate"); -/** @internal */ -var StableFilterAnnotationId = /* @__PURE__ */ Symbol.for("effect/annotation/StableFilter"); -/** -* @category annotations -* @since 3.10.0 -*/ -var getAnnotation = /* @__PURE__ */ require_NodeRuntime.dual(2, (annotated, key) => Object.prototype.hasOwnProperty.call(annotated.annotations, key) ? require_NodeRuntime.some(annotated.annotations[key]) : require_NodeRuntime.none$1()); -/** -* @category annotations -* @since 3.10.0 -*/ -var getBrandAnnotation = /* @__PURE__ */ getAnnotation(BrandAnnotationId); -/** -* @category annotations -* @since 3.10.0 -*/ -var getMessageAnnotation = /* @__PURE__ */ getAnnotation(MessageAnnotationId); -/** -* @category annotations -* @since 3.10.0 -*/ -var getMissingMessageAnnotation = /* @__PURE__ */ getAnnotation(MissingMessageAnnotationId); -/** -* @category annotations -* @since 3.10.0 -*/ -var getTitleAnnotation = /* @__PURE__ */ getAnnotation(TitleAnnotationId); -/** @internal */ -var getAutoTitleAnnotation = /* @__PURE__ */ getAnnotation(AutoTitleAnnotationId); -/** -* @category annotations -* @since 3.10.0 -*/ -var getIdentifierAnnotation = /* @__PURE__ */ getAnnotation(IdentifierAnnotationId); -/** -* @category annotations -* @since 3.10.0 -*/ -var getDescriptionAnnotation = /* @__PURE__ */ getAnnotation(DescriptionAnnotationId); -/** -* @category annotations -* @since 3.10.0 -*/ -var getConcurrencyAnnotation = /* @__PURE__ */ getAnnotation(ConcurrencyAnnotationId); -/** -* @category annotations -* @since 3.10.0 -*/ -var getBatchingAnnotation = /* @__PURE__ */ getAnnotation(BatchingAnnotationId); -/** -* @category annotations -* @since 3.10.0 -*/ -var getParseIssueTitleAnnotation$1 = /* @__PURE__ */ getAnnotation(ParseIssueTitleAnnotationId); -/** -* @category annotations -* @since 3.10.0 -*/ -var getParseOptionsAnnotation = /* @__PURE__ */ getAnnotation(ParseOptionsAnnotationId); -/** -* @category annotations -* @since 3.10.0 -*/ -var getDecodingFallbackAnnotation = /* @__PURE__ */ getAnnotation(DecodingFallbackAnnotationId); -/** -* @category annotations -* @since 3.10.0 -*/ -var getSurrogateAnnotation = /* @__PURE__ */ getAnnotation(SurrogateAnnotationId); -var getStableFilterAnnotation = /* @__PURE__ */ getAnnotation(StableFilterAnnotationId); -/** @internal */ -var hasStableFilter = (annotated) => require_NodeRuntime.exists(getStableFilterAnnotation(annotated), (b) => b === true); -/** -* @category annotations -* @since 3.10.0 -*/ -var JSONIdentifierAnnotationId = /* @__PURE__ */ Symbol.for("effect/annotation/JSONIdentifier"); -/** -* @category annotations -* @since 3.10.0 -*/ -var getJSONIdentifierAnnotation = /* @__PURE__ */ getAnnotation(JSONIdentifierAnnotationId); -/** -* @category annotations -* @since 3.10.0 -*/ -var getJSONIdentifier = (annotated) => require_NodeRuntime.orElse(getJSONIdentifierAnnotation(annotated), () => getIdentifierAnnotation(annotated)); -/** -* @category model -* @since 3.10.0 -*/ -var Declaration = class { - typeParameters; - decodeUnknown; - encodeUnknown; - annotations; - /** - * @since 3.10.0 - */ - _tag = "Declaration"; - constructor(typeParameters, decodeUnknown, encodeUnknown, annotations = {}) { - this.typeParameters = typeParameters; - this.decodeUnknown = decodeUnknown; - this.encodeUnknown = encodeUnknown; - this.annotations = annotations; - } - /** - * @since 3.10.0 - */ - toString() { - return require_NodeRuntime.getOrElse(getExpected(this), () => ""); - } - /** - * @since 3.10.0 - */ - toJSON() { - return { - _tag: this._tag, - typeParameters: this.typeParameters.map((ast) => ast.toJSON()), - annotations: toJSONAnnotations(this.annotations) - }; - } -}; -var createASTGuard = (tag) => (ast) => ast._tag === tag; -/** -* @category model -* @since 3.10.0 -*/ -var Literal$1 = class { - literal; - annotations; - /** - * @since 3.10.0 - */ - _tag = "Literal"; - constructor(literal, annotations = {}) { - this.literal = literal; - this.annotations = annotations; - } - /** - * @since 3.10.0 - */ - toString() { - return require_NodeRuntime.getOrElse(getExpected(this), () => require_NodeRuntime.formatUnknown(this.literal)); - } - /** - * @since 3.10.0 - */ - toJSON() { - return { - _tag: this._tag, - literal: require_NodeRuntime.isBigInt(this.literal) ? String(this.literal) : this.literal, - annotations: toJSONAnnotations(this.annotations) - }; - } -}; -/** -* @category guards -* @since 3.10.0 -*/ -var isLiteral = /* @__PURE__ */ createASTGuard("Literal"); -var $null = /* @__PURE__ */ new Literal$1(null); -/** -* @category model -* @since 3.10.0 -*/ -var UniqueSymbol = class { - symbol; - annotations; - /** - * @since 3.10.0 - */ - _tag = "UniqueSymbol"; - constructor(symbol, annotations = {}) { - this.symbol = symbol; - this.annotations = annotations; - } - /** - * @since 3.10.0 - */ - toString() { - return require_NodeRuntime.getOrElse(getExpected(this), () => require_NodeRuntime.formatUnknown(this.symbol)); - } - /** - * @since 3.10.0 - */ - toJSON() { - return { - _tag: this._tag, - symbol: String(this.symbol), - annotations: toJSONAnnotations(this.annotations) - }; - } -}; -/** -* @category model -* @since 3.10.0 -*/ -var UndefinedKeyword = class { - annotations; - /** - * @since 3.10.0 - */ - _tag = "UndefinedKeyword"; - constructor(annotations = {}) { - this.annotations = annotations; - } - /** - * @since 3.10.0 - */ - toString() { - return formatKeyword(this); - } - /** - * @since 3.10.0 - */ - toJSON() { - return { - _tag: this._tag, - annotations: toJSONAnnotations(this.annotations) - }; - } -}; -/** -* @category constructors -* @since 3.10.0 -*/ -var undefinedKeyword = /* @__PURE__ */ new UndefinedKeyword({ [TitleAnnotationId]: "undefined" }); -/** -* @category model -* @since 3.10.0 -*/ -var NeverKeyword = class { - annotations; - /** - * @since 3.10.0 - */ - _tag = "NeverKeyword"; - constructor(annotations = {}) { - this.annotations = annotations; - } - /** - * @since 3.10.0 - */ - toString() { - return formatKeyword(this); - } - /** - * @since 3.10.0 - */ - toJSON() { - return { - _tag: this._tag, - annotations: toJSONAnnotations(this.annotations) - }; - } -}; -/** -* @category constructors -* @since 3.10.0 -*/ -var neverKeyword = /* @__PURE__ */ new NeverKeyword({ [TitleAnnotationId]: "never" }); -/** -* @category model -* @since 3.10.0 -*/ -var UnknownKeyword = class { - annotations; - /** - * @since 3.10.0 - */ - _tag = "UnknownKeyword"; - constructor(annotations = {}) { - this.annotations = annotations; - } - /** - * @since 3.10.0 - */ - toString() { - return formatKeyword(this); - } - /** - * @since 3.10.0 - */ - toJSON() { - return { - _tag: this._tag, - annotations: toJSONAnnotations(this.annotations) - }; - } -}; -/** -* @category constructors -* @since 3.10.0 -*/ -var unknownKeyword = /* @__PURE__ */ new UnknownKeyword({ [TitleAnnotationId]: "unknown" }); -/** -* @category model -* @since 3.10.0 -*/ -var AnyKeyword = class { - annotations; - /** - * @since 3.10.0 - */ - _tag = "AnyKeyword"; - constructor(annotations = {}) { - this.annotations = annotations; - } - /** - * @since 3.10.0 - */ - toString() { - return formatKeyword(this); - } - /** - * @since 3.10.0 - */ - toJSON() { - return { - _tag: this._tag, - annotations: toJSONAnnotations(this.annotations) - }; - } -}; -/** -* @category constructors -* @since 3.10.0 -*/ -var anyKeyword = /* @__PURE__ */ new AnyKeyword({ [TitleAnnotationId]: "any" }); -/** -* @category model -* @since 3.10.0 -*/ -var StringKeyword = class { - annotations; - /** - * @since 3.10.0 - */ - _tag = "StringKeyword"; - constructor(annotations = {}) { - this.annotations = annotations; - } - /** - * @since 3.10.0 - */ - toString() { - return formatKeyword(this); - } - /** - * @since 3.10.0 - */ - toJSON() { - return { - _tag: this._tag, - annotations: toJSONAnnotations(this.annotations) - }; - } -}; -/** -* @category constructors -* @since 3.10.0 -*/ -var stringKeyword = /* @__PURE__ */ new StringKeyword({ - [TitleAnnotationId]: "string", - [DescriptionAnnotationId]: "a string" -}); -/** -* @category guards -* @since 3.10.0 -*/ -var isStringKeyword = /* @__PURE__ */ createASTGuard("StringKeyword"); -/** -* @category model -* @since 3.10.0 -*/ -var NumberKeyword = class { - annotations; - /** - * @since 3.10.0 - */ - _tag = "NumberKeyword"; - constructor(annotations = {}) { - this.annotations = annotations; - } - /** - * @since 3.10.0 - */ - toString() { - return formatKeyword(this); - } - /** - * @since 3.10.0 - */ - toJSON() { - return { - _tag: this._tag, - annotations: toJSONAnnotations(this.annotations) - }; - } -}; -/** -* @category constructors -* @since 3.10.0 -*/ -var numberKeyword = /* @__PURE__ */ new NumberKeyword({ - [TitleAnnotationId]: "number", - [DescriptionAnnotationId]: "a number" -}); -/** -* @category model -* @since 3.10.0 -*/ -var BooleanKeyword = class { - annotations; - /** - * @since 3.10.0 - */ - _tag = "BooleanKeyword"; - constructor(annotations = {}) { - this.annotations = annotations; - } - /** - * @since 3.10.0 - */ - toString() { - return formatKeyword(this); - } - /** - * @since 3.10.0 - */ - toJSON() { - return { - _tag: this._tag, - annotations: toJSONAnnotations(this.annotations) - }; - } -}; -/** -* @category constructors -* @since 3.10.0 -*/ -var booleanKeyword = /* @__PURE__ */ new BooleanKeyword({ - [TitleAnnotationId]: "boolean", - [DescriptionAnnotationId]: "a boolean" -}); -/** -* @category model -* @since 3.10.0 -*/ -var BigIntKeyword = class { - annotations; - /** - * @since 3.10.0 - */ - _tag = "BigIntKeyword"; - constructor(annotations = {}) { - this.annotations = annotations; - } - /** - * @since 3.10.0 - */ - toString() { - return formatKeyword(this); - } - /** - * @since 3.10.0 - */ - toJSON() { - return { - _tag: this._tag, - annotations: toJSONAnnotations(this.annotations) - }; - } -}; -/** -* @category constructors -* @since 3.10.0 -*/ -var bigIntKeyword = /* @__PURE__ */ new BigIntKeyword({ - [TitleAnnotationId]: "bigint", - [DescriptionAnnotationId]: "a bigint" -}); -/** -* @category model -* @since 3.10.0 -*/ -var SymbolKeyword = class { - annotations; - /** - * @since 3.10.0 - */ - _tag = "SymbolKeyword"; - constructor(annotations = {}) { - this.annotations = annotations; - } - /** - * @since 3.10.0 - */ - toString() { - return formatKeyword(this); - } - /** - * @since 3.10.0 - */ - toJSON() { - return { - _tag: this._tag, - annotations: toJSONAnnotations(this.annotations) - }; - } -}; -/** -* @category constructors -* @since 3.10.0 -*/ -var symbolKeyword = /* @__PURE__ */ new SymbolKeyword({ - [TitleAnnotationId]: "symbol", - [DescriptionAnnotationId]: "a symbol" -}); -/** -* @category guards -* @since 3.10.0 -*/ -var isSymbolKeyword = /* @__PURE__ */ createASTGuard("SymbolKeyword"); -/** -* @category model -* @since 3.10.0 -*/ -var Type$1 = class { - type; - annotations; - constructor(type, annotations = {}) { - this.type = type; - this.annotations = annotations; - } - /** - * @since 3.10.0 - */ - toJSON() { - return { - type: this.type.toJSON(), - annotations: toJSONAnnotations(this.annotations) - }; - } - /** - * @since 3.10.0 - */ - toString() { - return String(this.type); - } -}; -/** -* @category model -* @since 3.10.0 -*/ -var OptionalType = class extends Type$1 { - isOptional; - constructor(type, isOptional, annotations = {}) { - super(type, annotations); - this.isOptional = isOptional; - } - /** - * @since 3.10.0 - */ - toJSON() { - return { - type: this.type.toJSON(), - isOptional: this.isOptional, - annotations: toJSONAnnotations(this.annotations) - }; - } - /** - * @since 3.10.0 - */ - toString() { - return String(this.type) + (this.isOptional ? "?" : ""); - } -}; -var getRestASTs = (rest) => rest.map((annotatedAST) => annotatedAST.type); -/** -* @category model -* @since 3.10.0 -*/ -var TupleType = class { - elements; - rest; - isReadonly; - annotations; - /** - * @since 3.10.0 - */ - _tag = "TupleType"; - constructor(elements, rest, isReadonly, annotations = {}) { - this.elements = elements; - this.rest = rest; - this.isReadonly = isReadonly; - this.annotations = annotations; - let hasOptionalElement = false; - let hasIllegalRequiredElement = false; - for (const e of elements) if (e.isOptional) hasOptionalElement = true; - else if (hasOptionalElement) { - hasIllegalRequiredElement = true; - break; - } - if (hasIllegalRequiredElement || hasOptionalElement && rest.length > 1) throw new Error(getASTRequiredElementFollowinAnOptionalElementErrorMessage); - } - /** - * @since 3.10.0 - */ - toString() { - return require_NodeRuntime.getOrElse(getExpected(this), () => formatTuple(this)); - } - /** - * @since 3.10.0 - */ - toJSON() { - return { - _tag: this._tag, - elements: this.elements.map((e) => e.toJSON()), - rest: this.rest.map((ast) => ast.toJSON()), - isReadonly: this.isReadonly, - annotations: toJSONAnnotations(this.annotations) - }; - } -}; -var formatTuple = (ast) => { - const formattedElements = ast.elements.map(String).join(", "); - return require_NodeRuntime.matchLeft(ast.rest, { - onEmpty: () => `readonly [${formattedElements}]`, - onNonEmpty: (head, tail) => { - const formattedHead = String(head); - const wrappedHead = formattedHead.includes(" | ") ? `(${formattedHead})` : formattedHead; - if (tail.length > 0) { - const formattedTail = tail.map(String).join(", "); - if (ast.elements.length > 0) return `readonly [${formattedElements}, ...${wrappedHead}[], ${formattedTail}]`; - else return `readonly [...${wrappedHead}[], ${formattedTail}]`; - } else if (ast.elements.length > 0) return `readonly [${formattedElements}, ...${wrappedHead}[]]`; - else return `ReadonlyArray<${formattedHead}>`; - } - }); -}; -/** -* @category model -* @since 3.10.0 -*/ -var PropertySignature = class extends OptionalType { - name; - isReadonly; - constructor(name, type, isOptional, isReadonly, annotations) { - super(type, isOptional, annotations); - this.name = name; - this.isReadonly = isReadonly; - } - /** - * @since 3.10.0 - */ - toString() { - return (this.isReadonly ? "readonly " : "") + String(this.name) + (this.isOptional ? "?" : "") + ": " + this.type; - } - /** - * @since 3.10.0 - */ - toJSON() { - return { - name: String(this.name), - type: this.type.toJSON(), - isOptional: this.isOptional, - isReadonly: this.isReadonly, - annotations: toJSONAnnotations(this.annotations) - }; - } -}; -/** -* @since 3.10.0 -*/ -var isParameter = (ast) => { - switch (ast._tag) { - case "StringKeyword": - case "SymbolKeyword": - case "TemplateLiteral": return true; - case "Refinement": return isParameter(ast.from); - } - return false; -}; -/** -* @category model -* @since 3.10.0 -*/ -var IndexSignature = class { - type; - isReadonly; - /** - * @since 3.10.0 - */ - parameter; - constructor(parameter, type, isReadonly) { - this.type = type; - this.isReadonly = isReadonly; - if (isParameter(parameter)) this.parameter = parameter; - else throw new Error(getASTIndexSignatureParameterErrorMessage); - } - /** - * @since 3.10.0 - */ - toString() { - return (this.isReadonly ? "readonly " : "") + `[x: ${this.parameter}]: ${this.type}`; - } - /** - * @since 3.10.0 - */ - toJSON() { - return { - parameter: this.parameter.toJSON(), - type: this.type.toJSON(), - isReadonly: this.isReadonly - }; - } -}; -/** -* @category model -* @since 3.10.0 -*/ -var TypeLiteral = class { - annotations; - /** - * @since 3.10.0 - */ - _tag = "TypeLiteral"; - /** - * @since 3.10.0 - */ - propertySignatures; - /** - * @since 3.10.0 - */ - indexSignatures; - constructor(propertySignatures, indexSignatures, annotations = {}) { - this.annotations = annotations; - const keys = {}; - for (let i = 0; i < propertySignatures.length; i++) { - const name = propertySignatures[i].name; - if (Object.prototype.hasOwnProperty.call(keys, name)) throw new Error(getASTDuplicatePropertySignatureErrorMessage(name)); - keys[name] = null; - } - const parameters = { - string: false, - symbol: false - }; - for (let i = 0; i < indexSignatures.length; i++) { - const encodedParameter = getEncodedParameter(indexSignatures[i].parameter); - if (isStringKeyword(encodedParameter)) { - if (parameters.string) throw new Error(getASTDuplicateIndexSignatureErrorMessage("string")); - parameters.string = true; - } else if (isSymbolKeyword(encodedParameter)) { - if (parameters.symbol) throw new Error(getASTDuplicateIndexSignatureErrorMessage("symbol")); - parameters.symbol = true; - } - } - this.propertySignatures = propertySignatures; - this.indexSignatures = indexSignatures; - } - /** - * @since 3.10.0 - */ - toString() { - return require_NodeRuntime.getOrElse(getExpected(this), () => formatTypeLiteral(this)); - } - /** - * @since 3.10.0 - */ - toJSON() { - return { - _tag: this._tag, - propertySignatures: this.propertySignatures.map((ps) => ps.toJSON()), - indexSignatures: this.indexSignatures.map((ps) => ps.toJSON()), - annotations: toJSONAnnotations(this.annotations) - }; - } -}; -var formatIndexSignatures = (iss) => iss.map(String).join("; "); -var formatTypeLiteral = (ast) => { - if (ast.propertySignatures.length > 0) { - const pss = ast.propertySignatures.map(String).join("; "); - if (ast.indexSignatures.length > 0) return `{ ${pss}; ${formatIndexSignatures(ast.indexSignatures)} }`; - else return `{ ${pss} }`; - } else if (ast.indexSignatures.length > 0) return `{ ${formatIndexSignatures(ast.indexSignatures)} }`; - else return "{}"; -}; -var sortCandidates = /* @__PURE__ */ require_NodeRuntime.sort(/* @__PURE__ */ require_NodeRuntime.mapInput(require_NodeRuntime.Order, (ast) => { - switch (ast._tag) { - case "AnyKeyword": return 0; - case "UnknownKeyword": return 1; - case "ObjectKeyword": return 2; - case "StringKeyword": - case "NumberKeyword": - case "BooleanKeyword": - case "BigIntKeyword": - case "SymbolKeyword": return 3; - } - return 4; -})); -var literalMap = { - string: "StringKeyword", - number: "NumberKeyword", - boolean: "BooleanKeyword", - bigint: "BigIntKeyword" -}; -/** @internal */ -var flatten = (candidates) => require_NodeRuntime.flatMap$1(candidates, (ast) => isUnion(ast) ? flatten(ast.types) : [ast]); -/** @internal */ -var unify = (candidates) => { - const cs = sortCandidates(candidates); - const out = []; - const uniques = {}; - const literals = []; - for (const ast of cs) switch (ast._tag) { - case "NeverKeyword": break; - case "AnyKeyword": return [anyKeyword]; - case "UnknownKeyword": return [unknownKeyword]; - case "ObjectKeyword": - case "UndefinedKeyword": - case "VoidKeyword": - case "StringKeyword": - case "NumberKeyword": - case "BooleanKeyword": - case "BigIntKeyword": - case "SymbolKeyword": - if (!uniques[ast._tag]) { - uniques[ast._tag] = ast; - out.push(ast); - } - break; - case "Literal": { - const type = typeof ast.literal; - switch (type) { - case "string": - case "number": - case "bigint": - case "boolean": - if (!uniques[literalMap[type]] && !literals.includes(ast.literal)) { - literals.push(ast.literal); - out.push(ast); - } - break; - case "object": - if (!literals.includes(ast.literal)) { - literals.push(ast.literal); - out.push(ast); - } - break; - } - break; - } - case "UniqueSymbol": - if (!uniques["SymbolKeyword"] && !literals.includes(ast.symbol)) { - literals.push(ast.symbol); - out.push(ast); - } - break; - case "TupleType": - if (!uniques["ObjectKeyword"]) out.push(ast); - break; - case "TypeLiteral": - if (ast.propertySignatures.length === 0 && ast.indexSignatures.length === 0) { - if (!uniques["{}"]) { - uniques["{}"] = ast; - out.push(ast); - } - } else if (!uniques["ObjectKeyword"]) out.push(ast); - break; - default: out.push(ast); - } - return out; -}; -/** -* @category model -* @since 3.10.0 -*/ -var Union$1 = class Union$1 { - types; - annotations; - static make = (types, annotations) => { - return isMembers(types) ? new Union$1(types, annotations) : types.length === 1 ? types[0] : neverKeyword; - }; - /** @internal */ - static unify = (candidates, annotations) => { - return Union$1.make(unify(flatten(candidates)), annotations); - }; - /** - * @since 3.10.0 - */ - _tag = "Union"; - constructor(types, annotations = {}) { - this.types = types; - this.annotations = annotations; - } - /** - * @since 3.10.0 - */ - toString() { - return require_NodeRuntime.getOrElse(getExpected(this), () => this.types.map(String).join(" | ")); - } - /** - * @since 3.10.0 - */ - toJSON() { - return { - _tag: this._tag, - types: this.types.map((ast) => ast.toJSON()), - annotations: toJSONAnnotations(this.annotations) - }; - } -}; -/** @internal */ -var mapMembers = (members, f) => members.map(f); -/** @internal */ -var isMembers = (as) => as.length > 1; -/** -* @category guards -* @since 3.10.0 -*/ -var isUnion = /* @__PURE__ */ createASTGuard("Union"); -var toJSONMemoMap = /* @__PURE__ */ require_NodeRuntime.globalValue(/* @__PURE__ */ Symbol.for("effect/Schema/AST/toJSONMemoMap"), () => /* @__PURE__ */ new WeakMap()); -/** -* @category model -* @since 3.10.0 -*/ -var Suspend = class { - f; - annotations; - /** - * @since 3.10.0 - */ - _tag = "Suspend"; - constructor(f, annotations = {}) { - this.f = f; - this.annotations = annotations; - this.f = memoizeThunk(f); - } - /** - * @since 3.10.0 - */ - toString() { - return getExpected(this).pipe(require_NodeRuntime.orElse(() => require_NodeRuntime.flatMap$2(require_NodeRuntime.liftThrowable(this.f)(), (ast) => getExpected(ast))), require_NodeRuntime.getOrElse(() => "")); - } - /** - * @since 3.10.0 - */ - toJSON() { - const ast = this.f(); - let out = toJSONMemoMap.get(ast); - if (out) return out; - toJSONMemoMap.set(ast, { _tag: this._tag }); - out = { - _tag: this._tag, - ast: ast.toJSON(), - annotations: toJSONAnnotations(this.annotations) - }; - toJSONMemoMap.set(ast, out); - return out; - } -}; -/** -* @category model -* @since 3.10.0 -*/ -var Refinement$1 = class { - from; - filter; - annotations; - /** - * @since 3.10.0 - */ - _tag = "Refinement"; - constructor(from, filter, annotations = {}) { - this.from = from; - this.filter = filter; - this.annotations = annotations; - } - /** - * @since 3.10.0 - */ - toString() { - return getIdentifierAnnotation(this).pipe(require_NodeRuntime.getOrElse(() => require_NodeRuntime.match(getOrElseExpected(this), { - onNone: () => `{ ${this.from} | filter }`, - onSome: (expected) => isRefinement$1(this.from) ? String(this.from) + " & " + expected : expected - }))); - } - /** - * @since 3.10.0 - */ - toJSON() { - return { - _tag: this._tag, - from: this.from.toJSON(), - annotations: toJSONAnnotations(this.annotations) - }; - } -}; -/** -* @category guards -* @since 3.10.0 -*/ -var isRefinement$1 = /* @__PURE__ */ createASTGuard("Refinement"); -/** -* @since 3.10.0 -*/ -var defaultParseOption = {}; -/** -* @category model -* @since 3.10.0 -*/ -var Transformation$1 = class { - from; - to; - transformation; - annotations; - /** - * @since 3.10.0 - */ - _tag = "Transformation"; - constructor(from, to, transformation, annotations = {}) { - this.from = from; - this.to = to; - this.transformation = transformation; - this.annotations = annotations; - } - /** - * @since 3.10.0 - */ - toString() { - return require_NodeRuntime.getOrElse(getExpected(this), () => `(${String(this.from)} <-> ${String(this.to)})`); - } - /** - * @since 3.10.0 - */ - toJSON() { - return { - _tag: this._tag, - from: this.from.toJSON(), - to: this.to.toJSON(), - annotations: toJSONAnnotations(this.annotations) - }; - } -}; -/** -* @category model -* @since 3.10.0 -*/ -var FinalTransformation = class { - decode; - encode; - /** - * @since 3.10.0 - */ - _tag = "FinalTransformation"; - constructor(decode, encode) { - this.decode = decode; - this.encode = encode; - } -}; -/** -* Represents a `PropertySignature -> PropertySignature` transformation -* -* The semantic of `decode` is: -* - `none()` represents the absence of the key/value pair -* - `some(value)` represents the presence of the key/value pair -* -* The semantic of `encode` is: -* - `none()` you don't want to output the key/value pair -* - `some(value)` you want to output the key/value pair -* -* @category model -* @since 3.10.0 -*/ -var PropertySignatureTransformation$1 = class { - from; - to; - decode; - encode; - constructor(from, to, decode, encode) { - this.from = from; - this.to = to; - this.decode = decode; - this.encode = encode; - } -}; -/** -* @category model -* @since 3.10.0 -*/ -var TypeLiteralTransformation = class { - propertySignatureTransformations; - /** - * @since 3.10.0 - */ - _tag = "TypeLiteralTransformation"; - constructor(propertySignatureTransformations) { - this.propertySignatureTransformations = propertySignatureTransformations; - const fromKeys = {}; - const toKeys = {}; - for (const pst of propertySignatureTransformations) { - const from = pst.from; - if (fromKeys[from]) throw new Error(getASTDuplicatePropertySignatureTransformationErrorMessage(from)); - fromKeys[from] = true; - const to = pst.to; - if (toKeys[to]) throw new Error(getASTDuplicatePropertySignatureTransformationErrorMessage(to)); - toKeys[to] = true; - } - } -}; -/** -* Merges a set of new annotations with existing ones, potentially overwriting -* any duplicates. -* -* Any previously existing identifier annotations are deleted. -* -* @since 3.10.0 -*/ -var annotations = (ast, overrides) => { - const d = Object.getOwnPropertyDescriptors(ast); - const base = { ...ast.annotations }; - delete base[IdentifierAnnotationId]; - const value = { - ...base, - ...overrides - }; - const surrogate = getSurrogateAnnotation(ast); - if (require_NodeRuntime.isSome(surrogate)) value[SurrogateAnnotationId] = annotations(surrogate.value, overrides); - d.annotations.value = value; - return Object.create(Object.getPrototypeOf(ast), d); -}; -var STRING_KEYWORD_PATTERN = "[\\s\\S]*?"; -var NUMBER_KEYWORD_PATTERN = "[+-]?\\d*\\.?\\d+(?:[Ee][+-]?\\d+)?"; -var getTemplateLiteralSpanTypePattern = (type, capture) => { - switch (type._tag) { - case "Literal": return require_NodeRuntime.escape(String(type.literal)); - case "StringKeyword": return STRING_KEYWORD_PATTERN; - case "NumberKeyword": return NUMBER_KEYWORD_PATTERN; - case "TemplateLiteral": return getTemplateLiteralPattern(type, capture, false); - case "Union": return type.types.map((type) => getTemplateLiteralSpanTypePattern(type, capture)).join("|"); - } -}; -var handleTemplateLiteralSpanTypeParens = (type, s, capture, top) => { - if (isUnion(type)) { - if (capture && !top) return `(?:${s})`; - } else if (!capture || !top) return s; - return `(${s})`; -}; -var getTemplateLiteralPattern = (ast, capture, top) => { - let pattern = ``; - if (ast.head !== "") { - const head = require_NodeRuntime.escape(ast.head); - pattern += capture && top ? `(${head})` : head; - } - for (const span of ast.spans) { - const spanPattern = getTemplateLiteralSpanTypePattern(span.type, capture); - pattern += handleTemplateLiteralSpanTypeParens(span.type, spanPattern, capture, top); - if (span.literal !== "") { - const literal = require_NodeRuntime.escape(span.literal); - pattern += capture && top ? `(${literal})` : literal; - } - } - return pattern; -}; -/** -* Generates a regular expression from a `TemplateLiteral` AST node. -* -* @see {@link getTemplateLiteralCapturingRegExp} for a variant that captures the pattern. -* -* @since 3.10.0 -*/ -var getTemplateLiteralRegExp = (ast) => new RegExp(`^${getTemplateLiteralPattern(ast, false, true)}$`); -/** @internal */ -var record = (key, value) => { - const propertySignatures = []; - const indexSignatures = []; - const go = (key) => { - switch (key._tag) { - case "NeverKeyword": break; - case "StringKeyword": - case "SymbolKeyword": - case "TemplateLiteral": - case "Refinement": - indexSignatures.push(new IndexSignature(key, value, true)); - break; - case "Literal": - if (require_NodeRuntime.isString(key.literal) || require_NodeRuntime.isNumber(key.literal)) propertySignatures.push(new PropertySignature(key.literal, value, false, true)); - else throw new Error(getASTUnsupportedLiteralErrorMessage(key.literal)); - break; - case "Enums": - for (const [_, name] of key.enums) propertySignatures.push(new PropertySignature(name, value, false, true)); - break; - case "UniqueSymbol": - propertySignatures.push(new PropertySignature(key.symbol, value, false, true)); - break; - case "Union": - key.types.forEach(go); - break; - default: throw new Error(getASTUnsupportedKeySchemaErrorMessage(key)); - } - }; - go(key); +node_process = require_Layer.__toESM(node_process); +let node_crypto = require("node:crypto"); +//#region src/native-host/host.ts +var socketPath = node_process.default.env.OPZERO_CHROME_HOST_SOCKET || node_path.default.join(node_os.default.homedir(), ".opzero-chrome", "default.sock"); +var useTcp = node_process.default.platform === "win32" || node_process.default.env.OPZERO_CHROME_HOST_TRANSPORT === "tcp"; +var port = Number(node_process.default.env.OPZERO_CHROME_HOST_PORT || 17365); +var epoch = (0, node_crypto.randomUUID)(); +var protocolRequestId = `protocol:${epoch}`; +var extensionProtocol = "checking"; +var protocolTimer = setTimeout(() => { + extensionProtocol = "unsupported"; +}, 1e4); +protocolTimer.unref(); +var maxBytes = 1024 * 1024; +var maxResponseBytes = 64 * maxBytes; +var clients = /* @__PURE__ */ new Map(); +var pending = /* @__PURE__ */ new Map(); +var nextId = 1; +var ownsSocket = false; +var tcpToken; +function native(message) { + const body = Buffer.from(JSON.stringify(message)); + if (body.length > maxBytes) return false; + const header = Buffer.alloc(4); + header.writeUInt32LE(body.length); + node_process.default.stdout.write(Buffer.concat([header, body])); + return true; +} +function result(id, value) { return { - propertySignatures, - indexSignatures + jsonrpc: "2.0", + id, + result: value }; -}; -/** @internal */ -var pickAnnotations = (annotationIds) => (annotated) => { - let out = void 0; - for (const id of annotationIds) if (Object.prototype.hasOwnProperty.call(annotated.annotations, id)) { - if (out === void 0) out = {}; - out[id] = annotated.annotations[id]; - } - return out; -}; -var preserveTransformationAnnotations = /* @__PURE__ */ pickAnnotations([ - ExamplesAnnotationId, - DefaultAnnotationId, - JSONSchemaAnnotationId, - ArbitraryAnnotationId, - PrettyAnnotationId, - EquivalenceAnnotationId -]); -/** -* @since 3.10.0 -*/ -var typeAST = (ast) => { - switch (ast._tag) { - case "Declaration": { - const typeParameters = changeMap(ast.typeParameters, typeAST); - return typeParameters === ast.typeParameters ? ast : new Declaration(typeParameters, ast.decodeUnknown, ast.encodeUnknown, ast.annotations); - } - case "TupleType": { - const elements = changeMap(ast.elements, (e) => { - const type = typeAST(e.type); - return type === e.type ? e : new OptionalType(type, e.isOptional); - }); - const restASTs = getRestASTs(ast.rest); - const rest = changeMap(restASTs, typeAST); - return elements === ast.elements && rest === restASTs ? ast : new TupleType(elements, rest.map((type) => new Type$1(type)), ast.isReadonly, ast.annotations); - } - case "TypeLiteral": { - const propertySignatures = changeMap(ast.propertySignatures, (p) => { - const type = typeAST(p.type); - return type === p.type ? p : new PropertySignature(p.name, type, p.isOptional, p.isReadonly); - }); - const indexSignatures = changeMap(ast.indexSignatures, (is) => { - const type = typeAST(is.type); - return type === is.type ? is : new IndexSignature(is.parameter, type, is.isReadonly); - }); - return propertySignatures === ast.propertySignatures && indexSignatures === ast.indexSignatures ? ast : new TypeLiteral(propertySignatures, indexSignatures, ast.annotations); - } - case "Union": { - const types = changeMap(ast.types, typeAST); - return types === ast.types ? ast : Union$1.make(types, ast.annotations); - } - case "Suspend": return new Suspend(() => typeAST(ast.f()), ast.annotations); - case "Refinement": { - const from = typeAST(ast.from); - return from === ast.from ? ast : new Refinement$1(from, ast.filter, ast.annotations); - } - case "Transformation": { - const preserve = preserveTransformationAnnotations(ast); - return typeAST(preserve !== void 0 ? annotations(ast.to, preserve) : ast.to); - } - } - return ast; -}; -function changeMap(as, f) { - let changed = false; - const out = require_NodeRuntime.allocate(as.length); - for (let i = 0; i < as.length; i++) { - const a = as[i]; - const fa = f(a); - if (fa !== a) changed = true; - out[i] = fa; - } - return changed ? out : as; } -/** -* Returns the from part of a transformation if it exists -* -* @internal -*/ -var getTransformationFrom = (ast) => { - switch (ast._tag) { - case "Transformation": return ast.from; - case "Refinement": return getTransformationFrom(ast.from); - case "Suspend": return getTransformationFrom(ast.f()); - } -}; -var encodedAST_ = (ast, isBound) => { - switch (ast._tag) { - case "Declaration": { - const typeParameters = changeMap(ast.typeParameters, (ast) => encodedAST_(ast, isBound)); - return typeParameters === ast.typeParameters ? ast : new Declaration(typeParameters, ast.decodeUnknown, ast.encodeUnknown); - } - case "TupleType": { - const elements = changeMap(ast.elements, (e) => { - const type = encodedAST_(e.type, isBound); - return type === e.type ? e : new OptionalType(type, e.isOptional); - }); - const restASTs = getRestASTs(ast.rest); - const rest = changeMap(restASTs, (ast) => encodedAST_(ast, isBound)); - return elements === ast.elements && rest === restASTs ? ast : new TupleType(elements, rest.map((ast) => new Type$1(ast)), ast.isReadonly); - } - case "TypeLiteral": { - const propertySignatures = changeMap(ast.propertySignatures, (ps) => { - const type = encodedAST_(ps.type, isBound); - return type === ps.type ? ps : new PropertySignature(ps.name, type, ps.isOptional, ps.isReadonly); - }); - const indexSignatures = changeMap(ast.indexSignatures, (is) => { - const type = encodedAST_(is.type, isBound); - return type === is.type ? is : new IndexSignature(is.parameter, type, is.isReadonly); - }); - return propertySignatures === ast.propertySignatures && indexSignatures === ast.indexSignatures ? ast : new TypeLiteral(propertySignatures, indexSignatures); - } - case "Union": { - const types = changeMap(ast.types, (ast) => encodedAST_(ast, isBound)); - return types === ast.types ? ast : Union$1.make(types); - } - case "Suspend": { - let borrowedAnnotations = void 0; - const identifier = getJSONIdentifier(ast); - if (require_NodeRuntime.isSome(identifier)) { - const suffix = isBound ? "Bound" : ""; - borrowedAnnotations = { [JSONIdentifierAnnotationId]: `${identifier.value}Encoded${suffix}` }; - } - return new Suspend(() => encodedAST_(ast.f(), isBound), borrowedAnnotations); - } - case "Refinement": { - const from = encodedAST_(ast.from, isBound); - if (isBound) { - if (from === ast.from) return ast; - if (getTransformationFrom(ast.from) === void 0 && hasStableFilter(ast)) return new Refinement$1(from, ast.filter, ast.annotations); - return from; - } else return from; - } - case "Transformation": return encodedAST_(ast.from, isBound); - } - return ast; -}; -/** -* @since 3.10.0 -*/ -var encodedAST = (ast) => encodedAST_(ast, false); -var toJSONAnnotations = (annotations) => { - const out = {}; - for (const k of Object.getOwnPropertySymbols(annotations)) out[String(k)] = annotations[k]; - return out; -}; -/** @internal */ -var getEncodedParameter = (ast) => { - switch (ast._tag) { - case "StringKeyword": - case "SymbolKeyword": - case "TemplateLiteral": return ast; - case "Refinement": return getEncodedParameter(ast.from); - } -}; -var formatKeyword = (ast) => require_NodeRuntime.getOrElse(getExpected(ast), () => ast._tag); -function getBrands(ast) { - return require_NodeRuntime.match(getBrandAnnotation(ast), { - onNone: () => "", - onSome: (brands) => brands.map((brand) => ` & Brand<${require_NodeRuntime.formatUnknown(brand)}>`).join("") - }); -} -var getOrElseExpected = (ast) => getTitleAnnotation(ast).pipe(require_NodeRuntime.orElse(() => getDescriptionAnnotation(ast)), require_NodeRuntime.orElse(() => getAutoTitleAnnotation(ast)), require_NodeRuntime.map$1((s) => s + getBrands(ast))); -var getExpected = (ast) => require_NodeRuntime.orElse(getIdentifierAnnotation(ast), () => getOrElseExpected(ast)); -//#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/BigDecimal.js -/** -* This module provides utility functions and type class instances for working with the `BigDecimal` type in TypeScript. -* It includes functions for basic arithmetic operations, as well as type class instances for `Equivalence` and `Order`. -* -* A `BigDecimal` allows storing any real number to arbitrary precision; which avoids common floating point errors -* (such as 0.1 + 0.2 ≠ 0.3) at the cost of complexity. -* -* Internally, `BigDecimal` uses a `BigInt` object, paired with a 64-bit integer which determines the position of the -* decimal point. Therefore, the precision *is not* actually arbitrary, but limited to 263 decimal places. -* -* It is not recommended to convert a floating point number to a decimal directly, as the floating point representation -* may be unexpected. -* -* @module BigDecimal -* @since 2.0.0 -* @see {@link module:BigInt} for more similar operations on `bigint` types -* @see {@link module:Number} for more similar operations on `number` types -*/ -var FINITE_INT_REGEX = /^[+-]?\d+$/; -/** -* @since 2.0.0 -* @category symbols -*/ -var TypeId$2 = /* @__PURE__ */ Symbol.for("effect/BigDecimal"); -var BigDecimalProto = { - [TypeId$2]: TypeId$2, - [require_NodeRuntime.symbol$1]() { - const normalized = normalize(this); - return require_NodeRuntime.pipe(require_NodeRuntime.hash(normalized.value), require_NodeRuntime.combine(require_NodeRuntime.number(normalized.scale)), require_NodeRuntime.cached(this)); - }, - [require_NodeRuntime.symbol](that) { - return isBigDecimal(that) && equals(this, that); - }, - toString() { - return `BigDecimal(${format$1(this)})`; - }, - toJSON() { - return { - _id: "BigDecimal", - value: String(this.value), - scale: this.scale - }; - }, - [require_NodeRuntime.NodeInspectSymbol]() { - return this.toJSON(); - }, - pipe() { - return require_NodeRuntime.pipeArguments(this, arguments); - } -}; -/** -* Checks if a given value is a `BigDecimal`. -* -* @since 2.0.0 -* @category guards -*/ -var isBigDecimal = (u) => require_NodeRuntime.hasProperty(u, TypeId$2); -/** -* Creates a `BigDecimal` from a `bigint` value and a scale. -* -* @since 2.0.0 -* @category constructors -*/ -var make$1 = (value, scale) => { - const o = Object.create(BigDecimalProto); - o.value = value; - o.scale = scale; - return o; -}; -/** -* Internal function used to create pre-normalized `BigDecimal`s. -* -* @internal -*/ -var unsafeMakeNormalized = (value, scale) => { - if (value !== bigint0 && value % bigint10 === bigint0) throw new RangeError("Value must be normalized"); - const o = make$1(value, scale); - o.normalized = o; - return o; -}; -var bigint0 = /* @__PURE__ */ BigInt(0); -var bigint10 = /* @__PURE__ */ BigInt(10); -var zero = /* @__PURE__ */ unsafeMakeNormalized(bigint0, 0); -/** -* Normalizes a given `BigDecimal` by removing trailing zeros. -* -* **Example** -* -* ```ts -* import * as assert from "node:assert" -* import { normalize, make, unsafeFromString } from "effect/BigDecimal" -* -* assert.deepStrictEqual(normalize(unsafeFromString("123.00000")), normalize(make(123n, 0))) -* assert.deepStrictEqual(normalize(unsafeFromString("12300000")), normalize(make(123n, -5))) -* ``` -* -* @since 2.0.0 -* @category scaling -*/ -var normalize = (self) => { - if (self.normalized === void 0) if (self.value === bigint0) self.normalized = zero; - else { - const digits = `${self.value}`; - let trail = 0; - for (let i = digits.length - 1; i >= 0; i--) if (digits[i] === "0") trail++; - else break; - if (trail === 0) self.normalized = self; - self.normalized = unsafeMakeNormalized(BigInt(digits.substring(0, digits.length - trail)), self.scale - trail); - } - return self.normalized; -}; -/** -* Scales a given `BigDecimal` to the specified scale. -* -* If the given scale is smaller than the current scale, the value will be rounded down to -* the nearest integer. -* -* @since 2.0.0 -* @category scaling -*/ -var scale = /* @__PURE__ */ require_NodeRuntime.dual(2, (self, scale) => { - if (scale > self.scale) return make$1(self.value * bigint10 ** BigInt(scale - self.scale), scale); - if (scale < self.scale) return make$1(self.value / bigint10 ** BigInt(self.scale - scale), scale); - return self; -}); -/** -* Determines the absolute value of a given `BigDecimal`. -* -* @example -* ```ts -* import * as assert from "node:assert" -* import { abs, unsafeFromString } from "effect/BigDecimal" -* -* assert.deepStrictEqual(abs(unsafeFromString("-5")), unsafeFromString("5")) -* assert.deepStrictEqual(abs(unsafeFromString("0")), unsafeFromString("0")) -* assert.deepStrictEqual(abs(unsafeFromString("5")), unsafeFromString("5")) -* ``` -* -* @since 2.0.0 -* @category math -*/ -var abs = (n) => n.value < bigint0 ? make$1(-n.value, n.scale) : n; -/** -* @category instances -* @since 2.0.0 -*/ -var Equivalence$2 = /* @__PURE__ */ require_NodeRuntime.make((self, that) => { - if (self.scale > that.scale) return scale(that, self.scale).value === self.value; - if (self.scale < that.scale) return scale(self, that.scale).value === that.value; - return self.value === that.value; -}); -/** -* Checks if two `BigDecimal`s are equal. -* -* @since 2.0.0 -* @category predicates -*/ -var equals = /* @__PURE__ */ require_NodeRuntime.dual(2, (self, that) => Equivalence$2(self, that)); -/** -* Creates a `BigDecimal` from a `number` value. -* -* It is not recommended to convert a floating point number to a decimal directly, -* as the floating point representation may be unexpected. -* -* Throws a `RangeError` if the number is not finite (`NaN`, `+Infinity` or `-Infinity`). -* -* @example -* ```ts -* import * as assert from "node:assert" -* import { unsafeFromNumber, make } from "effect/BigDecimal" -* -* assert.deepStrictEqual(unsafeFromNumber(123), make(123n, 0)) -* assert.deepStrictEqual(unsafeFromNumber(123.456), make(123456n, 3)) -* ``` -* -* @since 3.11.0 -* @category constructors -*/ -var unsafeFromNumber = (n) => require_NodeRuntime.getOrThrowWith(safeFromNumber(n), () => /* @__PURE__ */ new RangeError(`Number must be finite, got ${n}`)); -/** -* Creates a `BigDecimal` from a `number` value. -* -* It is not recommended to convert a floating point number to a decimal directly, -* as the floating point representation may be unexpected. -* -* Returns `None` if the number is not finite (`NaN`, `+Infinity` or `-Infinity`). -* -* @example -* ```ts -* import * as assert from "node:assert" -* import { BigDecimal, Option } from "effect" -* -* assert.deepStrictEqual(BigDecimal.safeFromNumber(123), Option.some(BigDecimal.make(123n, 0))) -* assert.deepStrictEqual(BigDecimal.safeFromNumber(123.456), Option.some(BigDecimal.make(123456n, 3))) -* assert.deepStrictEqual(BigDecimal.safeFromNumber(Infinity), Option.none()) -* ``` -* -* @since 3.11.0 -* @category constructors -*/ -var safeFromNumber = (n) => { - if (!Number.isFinite(n)) return require_NodeRuntime.none$1(); - const string = `${n}`; - if (string.includes("e")) return fromString$1(string); - const [lead, trail = ""] = string.split("."); - return require_NodeRuntime.some(make$1(BigInt(`${lead}${trail}`), trail.length)); -}; -/** -* Parses a numerical `string` into a `BigDecimal`. -* -* @example -* ```ts -* import * as assert from "node:assert" -* import { BigDecimal, Option } from "effect" -* -* assert.deepStrictEqual(BigDecimal.fromString("123"), Option.some(BigDecimal.make(123n, 0))) -* assert.deepStrictEqual(BigDecimal.fromString("123.456"), Option.some(BigDecimal.make(123456n, 3))) -* assert.deepStrictEqual(BigDecimal.fromString("123.abc"), Option.none()) -* ``` -* -* @since 2.0.0 -* @category constructors -*/ -var fromString$1 = (s) => { - if (s === "") return require_NodeRuntime.some(zero); - let base; - let exp; - const seperator = s.search(/[eE]/); - if (seperator !== -1) { - const trail = s.slice(seperator + 1); - base = s.slice(0, seperator); - exp = Number(trail); - if (base === "" || !Number.isSafeInteger(exp) || !FINITE_INT_REGEX.test(trail)) return require_NodeRuntime.none$1(); - } else { - base = s; - exp = 0; - } - let digits; - let offset; - const dot = base.search(/\./); - if (dot !== -1) { - const lead = base.slice(0, dot); - const trail = base.slice(dot + 1); - digits = `${lead}${trail}`; - offset = trail.length; - } else { - digits = base; - offset = 0; - } - if (!FINITE_INT_REGEX.test(digits)) return require_NodeRuntime.none$1(); - const scale = offset - exp; - if (!Number.isSafeInteger(scale)) return require_NodeRuntime.none$1(); - return require_NodeRuntime.some(make$1(BigInt(digits), scale)); -}; -/** -* Formats a given `BigDecimal` as a `string`. -* -* If the scale of the `BigDecimal` is greater than or equal to 16, the `BigDecimal` will -* be formatted in scientific notation. -* -* @example -* ```ts -* import * as assert from "node:assert" -* import { format, unsafeFromString } from "effect/BigDecimal" -* -* assert.deepStrictEqual(format(unsafeFromString("-5")), "-5") -* assert.deepStrictEqual(format(unsafeFromString("123.456")), "123.456") -* assert.deepStrictEqual(format(unsafeFromString("-0.00000123")), "-0.00000123") -* ``` -* -* @since 2.0.0 -* @category conversions -*/ -var format$1 = (n) => { - const normalized = normalize(n); - if (Math.abs(normalized.scale) >= 16) return toExponential(normalized); - const negative = normalized.value < bigint0; - const absolute = negative ? `${normalized.value}`.substring(1) : `${normalized.value}`; - let before; - let after; - if (normalized.scale >= absolute.length) { - before = "0"; - after = "0".repeat(normalized.scale - absolute.length) + absolute; - } else { - const location = absolute.length - normalized.scale; - if (location > absolute.length) { - const zeros = location - absolute.length; - before = `${absolute}${"0".repeat(zeros)}`; - after = ""; - } else { - after = absolute.slice(location); - before = absolute.slice(0, location); - } - } - const complete = after === "" ? before : `${before}.${after}`; - return negative ? `-${complete}` : complete; -}; -/** -* Formats a given `BigDecimal` as a `string` in scientific notation. -* -* @example -* ```ts -* import * as assert from "node:assert" -* import { toExponential, make } from "effect/BigDecimal" -* -* assert.deepStrictEqual(toExponential(make(123456n, -5)), "1.23456e+10") -* ``` -* -* @since 3.11.0 -* @category conversions -*/ -var toExponential = (n) => { - if (isZero(n)) return "0e+0"; - const normalized = normalize(n); - const digits = `${abs(normalized).value}`; - const head = digits.slice(0, 1); - const tail = digits.slice(1); - let output = `${isNegative(normalized) ? "-" : ""}${head}`; - if (tail !== "") output += `.${tail}`; - const exp = tail.length - normalized.scale; - return `${output}e${exp >= 0 ? "+" : ""}${exp}`; -}; -/** -* Converts a `BigDecimal` to a `number`. -* -* This function will produce incorrect results if the `BigDecimal` exceeds the 64-bit range of a `number`. -* -* @example -* ```ts -* import * as assert from "node:assert" -* import { unsafeToNumber, unsafeFromString } from "effect/BigDecimal" -* -* assert.deepStrictEqual(unsafeToNumber(unsafeFromString("123.456")), 123.456) -* ``` -* -* @since 2.0.0 -* @category conversions -*/ -var unsafeToNumber = (n) => Number(format$1(n)); -/** -* Checks if a given `BigDecimal` is `0`. -* -* @example -* ```ts -* import * as assert from "node:assert" -* import { isZero, unsafeFromString } from "effect/BigDecimal" -* -* assert.deepStrictEqual(isZero(unsafeFromString("0")), true) -* assert.deepStrictEqual(isZero(unsafeFromString("1")), false) -* ``` -* -* @since 2.0.0 -* @category predicates -*/ -var isZero = (n) => n.value === bigint0; -/** -* Checks if a given `BigDecimal` is negative. -* -* @example -* ```ts -* import * as assert from "node:assert" -* import { isNegative, unsafeFromString } from "effect/BigDecimal" -* -* assert.deepStrictEqual(isNegative(unsafeFromString("-1")), true) -* assert.deepStrictEqual(isNegative(unsafeFromString("0")), false) -* assert.deepStrictEqual(isNegative(unsafeFromString("1")), false) -* ``` -* -* @since 2.0.0 -* @category predicates -*/ -var isNegative = (n) => n.value < bigint0; -//#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/BigInt.js -/** -* Takes a `bigint` and returns an `Option` of `number`. -* -* If the `bigint` is outside the safe integer range for JavaScript (`Number.MAX_SAFE_INTEGER` -* and `Number.MIN_SAFE_INTEGER`), it returns `Option.none()`. Otherwise, it converts the `bigint` -* to a number and returns `Option.some(number)`. -* -* @example -* ```ts -* import * as assert from "node:assert" -* import { BigInt as BI, Option } from "effect" -* -* assert.deepStrictEqual(BI.toNumber(BigInt(42)), Option.some(42)) -* assert.deepStrictEqual(BI.toNumber(BigInt(Number.MAX_SAFE_INTEGER) + BigInt(1)), Option.none()) -* assert.deepStrictEqual(BI.toNumber(BigInt(Number.MIN_SAFE_INTEGER) - BigInt(1)), Option.none()) -* ``` -* -* @category conversions -* @since 2.0.0 -*/ -var toNumber = (b) => { - if (b > BigInt(Number.MAX_SAFE_INTEGER) || b < BigInt(Number.MIN_SAFE_INTEGER)) return require_NodeRuntime.none$1(); - return require_NodeRuntime.some(Number(b)); -}; -/** -* Takes a string and returns an `Option` of `bigint`. -* -* If the string is empty or contains characters that cannot be converted into a `bigint`, -* it returns `Option.none()`, otherwise, it returns `Option.some(bigint)`. -* -* @example -* ```ts -* import * as assert from "node:assert" -* import { BigInt as BI, Option } from "effect" -* -* assert.deepStrictEqual(BI.fromString("42"), Option.some(BigInt(42))) -* assert.deepStrictEqual(BI.fromString(" "), Option.none()) -* assert.deepStrictEqual(BI.fromString("a"), Option.none()) -* ``` -* -* @category conversions -* @since 2.4.12 -*/ -var fromString = (s) => { - try { - return s.trim() === "" ? require_NodeRuntime.none$1() : require_NodeRuntime.some(BigInt(s)); - } catch { - return require_NodeRuntime.none$1(); - } -}; -/** -* Takes a number and returns an `Option` of `bigint`. -* -* If the number is outside the safe integer range for JavaScript (`Number.MAX_SAFE_INTEGER` -* and `Number.MIN_SAFE_INTEGER`), it returns `Option.none()`. Otherwise, it attempts to -* convert the number to a `bigint` and returns `Option.some(bigint)`. -* -* @example -* ```ts -* import * as assert from "node:assert" -* import { BigInt as BI, Option } from "effect" -* -* assert.deepStrictEqual(BI.fromNumber(42), Option.some(BigInt(42))) -* assert.deepStrictEqual(BI.fromNumber(Number.MAX_SAFE_INTEGER + 1), Option.none()) -* assert.deepStrictEqual(BI.fromNumber(Number.MIN_SAFE_INTEGER - 1), Option.none()) -* ``` -* -* @category conversions -* @since 2.4.12 -*/ -var fromNumber = (n) => { - if (n > Number.MAX_SAFE_INTEGER || n < Number.MIN_SAFE_INTEGER) return require_NodeRuntime.none$1(); - try { - return require_NodeRuntime.some(BigInt(n)); - } catch { - return require_NodeRuntime.none$1(); - } -}; -//#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Data.js -/** -* Provides a constructor for a Case Class. -* -* @since 2.0.0 -* @category constructors -*/ -var Error$1 = /* @__PURE__ */ function() { - const plainArgsSymbol = /* @__PURE__ */ Symbol.for("effect/Data/Error/plainArgs"); - return { BaseEffectError: class extends require_NodeRuntime.YieldableError { - constructor(args) { - super(args?.message, args?.cause ? { cause: args.cause } : void 0); - if (args) { - Object.assign(this, args); - Object.defineProperty(this, plainArgsSymbol, { - value: args, - enumerable: false - }); - } - } - toJSON() { - return { - ...this[plainArgsSymbol], - ...this - }; - } - } }.BaseEffectError; -}(); -/** -* @since 2.0.0 -* @category constructors -*/ -var TaggedError = (tag) => { - const O = { BaseEffectError: class extends Error$1 { - _tag = tag; - } }; - O.BaseEffectError.prototype.name = tag; - return O.BaseEffectError; -}; -//#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/dateTime.js -/** @internal */ -var TypeId$1 = /* @__PURE__ */ Symbol.for("effect/DateTime"); -/** @internal */ -var TimeZoneTypeId = /* @__PURE__ */ Symbol.for("effect/DateTime/TimeZone"); -var Proto = { - [TypeId$1]: TypeId$1, - pipe() { - return require_NodeRuntime.pipeArguments(this, arguments); - }, - [require_NodeRuntime.NodeInspectSymbol]() { - return this.toString(); - }, - toJSON() { - return toDateUtc$1(this).toJSON(); - } -}; -var ProtoUtc = { - ...Proto, - _tag: "Utc", - [require_NodeRuntime.symbol$1]() { - return require_NodeRuntime.cached(this, require_NodeRuntime.number(this.epochMillis)); - }, - [require_NodeRuntime.symbol](that) { - return isDateTime$1(that) && that._tag === "Utc" && this.epochMillis === that.epochMillis; - }, - toString() { - return `DateTime.Utc(${toDateUtc$1(this).toJSON()})`; - } -}; -var ProtoZoned = { - ...Proto, - _tag: "Zoned", - [require_NodeRuntime.symbol$1]() { - return require_NodeRuntime.pipe(require_NodeRuntime.number(this.epochMillis), require_NodeRuntime.combine(require_NodeRuntime.hash(this.zone)), require_NodeRuntime.cached(this)); - }, - [require_NodeRuntime.symbol](that) { - return isDateTime$1(that) && that._tag === "Zoned" && this.epochMillis === that.epochMillis && require_NodeRuntime.equals(this.zone, that.zone); - }, - toString() { - return `DateTime.Zoned(${formatIsoZoned$1(this)})`; - } -}; -var ProtoTimeZone = { - [TimeZoneTypeId]: TimeZoneTypeId, - [require_NodeRuntime.NodeInspectSymbol]() { - return this.toString(); - } -}; -var ProtoTimeZoneNamed = { - ...ProtoTimeZone, - _tag: "Named", - [require_NodeRuntime.symbol$1]() { - return require_NodeRuntime.cached(this, require_NodeRuntime.string(`Named:${this.id}`)); - }, - [require_NodeRuntime.symbol](that) { - return isTimeZone(that) && that._tag === "Named" && this.id === that.id; - }, - toString() { - return `TimeZone.Named(${this.id})`; - }, - toJSON() { - return { - _id: "TimeZone", - _tag: "Named", - id: this.id - }; - } -}; -var ProtoTimeZoneOffset = { - ...ProtoTimeZone, - _tag: "Offset", - [require_NodeRuntime.symbol$1]() { - return require_NodeRuntime.cached(this, require_NodeRuntime.string(`Offset:${this.offset}`)); - }, - [require_NodeRuntime.symbol](that) { - return isTimeZone(that) && that._tag === "Offset" && this.offset === that.offset; - }, - toString() { - return `TimeZone.Offset(${offsetToString(this.offset)})`; - }, - toJSON() { - return { - _id: "TimeZone", - _tag: "Offset", - offset: this.offset - }; - } -}; -/** @internal */ -var makeZonedProto = (epochMillis, zone, partsUtc) => { - const self = Object.create(ProtoZoned); - self.epochMillis = epochMillis; - self.zone = zone; - Object.defineProperty(self, "partsUtc", { - value: partsUtc, - enumerable: false, - writable: true - }); - Object.defineProperty(self, "adjustedEpochMillis", { - value: void 0, - enumerable: false, - writable: true - }); - Object.defineProperty(self, "partsAdjusted", { - value: void 0, - enumerable: false, - writable: true - }); - return self; -}; -/** @internal */ -var isDateTime$1 = (u) => require_NodeRuntime.hasProperty(u, TypeId$1); -/** @internal */ -var isTimeZone = (u) => require_NodeRuntime.hasProperty(u, TimeZoneTypeId); -/** @internal */ -var isTimeZoneOffset$1 = (u) => isTimeZone(u) && u._tag === "Offset"; -/** @internal */ -var isTimeZoneNamed$1 = (u) => isTimeZone(u) && u._tag === "Named"; -/** @internal */ -var isUtc$1 = (self) => self._tag === "Utc"; -/** @internal */ -var isZoned$1 = (self) => self._tag === "Zoned"; -/** @internal */ -var Equivalence$1 = /* @__PURE__ */ require_NodeRuntime.make((a, b) => a.epochMillis === b.epochMillis); -var makeUtc = (epochMillis) => { - const self = Object.create(ProtoUtc); - self.epochMillis = epochMillis; - Object.defineProperty(self, "partsUtc", { - value: void 0, - enumerable: false, - writable: true - }); - return self; -}; -/** @internal */ -var unsafeFromDate$1 = (date) => { - const epochMillis = date.getTime(); - if (Number.isNaN(epochMillis)) throw new require_NodeRuntime.IllegalArgumentException("Invalid date"); - return makeUtc(epochMillis); -}; -/** @internal */ -var unsafeMake$1 = (input) => { - if (isDateTime$1(input)) return input; - else if (input instanceof Date) return unsafeFromDate$1(input); - else if (typeof input === "object") { - const date = /* @__PURE__ */ new Date(0); - setPartsDate(date, input); - return unsafeFromDate$1(date); - } else if (typeof input === "string" && !hasZone(input)) return unsafeFromDate$1(/* @__PURE__ */ new Date(input + "Z")); - return unsafeFromDate$1(new Date(input)); -}; -var hasZone = (input) => /Z|[+-]\d{2}$|[+-]\d{2}:?\d{2}$|\]$/.test(input); -var minEpochMillis = -86399999568e5; -var maxEpochMillis = 864e13 - 840 * 60 * 1e3; -/** @internal */ -var unsafeMakeZoned$1 = (input, options) => { - if (options?.timeZone === void 0 && isDateTime$1(input) && isZoned$1(input)) return input; - const self = unsafeMake$1(input); - if (self.epochMillis < minEpochMillis || self.epochMillis > maxEpochMillis) throw new RangeError(`Epoch millis out of range: ${self.epochMillis}`); - let zone; - if (options?.timeZone === void 0) zone = zoneMakeOffset$1(new Date(self.epochMillis).getTimezoneOffset() * -60 * 1e3); - else if (isTimeZone(options?.timeZone)) zone = options.timeZone; - else if (typeof options?.timeZone === "number") zone = zoneMakeOffset$1(options.timeZone); - else { - const parsedZone = zoneFromString$1(options.timeZone); - if (require_NodeRuntime.isNone(parsedZone)) throw new require_NodeRuntime.IllegalArgumentException(`Invalid time zone: ${options.timeZone}`); - zone = parsedZone.value; - } - if (options?.adjustForTimeZone !== true) return makeZonedProto(self.epochMillis, zone, self.partsUtc); - return makeZonedFromAdjusted(self.epochMillis, zone, options?.disambiguation ?? "compatible"); -}; -/** @internal */ -var makeZoned = /* @__PURE__ */ require_NodeRuntime.liftThrowable(unsafeMakeZoned$1); -var zonedStringRegex = /^(.{17,35})\[(.+)\]$/; -/** @internal */ -var makeZonedFromString$1 = (input) => { - const match = zonedStringRegex.exec(input); - if (match === null) { - const offset = parseOffset(input); - return offset !== null ? makeZoned(input, { timeZone: offset }) : require_NodeRuntime.none$1(); - } - const [, isoString, timeZone] = match; - return makeZoned(isoString, { timeZone }); -}; -var validZoneCache = /* @__PURE__ */ require_NodeRuntime.globalValue("effect/DateTime/validZoneCache", () => /* @__PURE__ */ new Map()); -var formatOptions = { - day: "numeric", - month: "numeric", - year: "numeric", - hour: "numeric", - minute: "numeric", - second: "numeric", - timeZoneName: "longOffset", - fractionalSecondDigits: 3, - hourCycle: "h23" -}; -var zoneMakeIntl = (format) => { - const zoneId = format.resolvedOptions().timeZone; - if (validZoneCache.has(zoneId)) return validZoneCache.get(zoneId); - const zone = Object.create(ProtoTimeZoneNamed); - zone.id = zoneId; - zone.format = format; - validZoneCache.set(zoneId, zone); - return zone; -}; -/** @internal */ -var zoneUnsafeMakeNamed$1 = (zoneId) => { - if (validZoneCache.has(zoneId)) return validZoneCache.get(zoneId); - try { - return zoneMakeIntl(new Intl.DateTimeFormat("en-US", { - ...formatOptions, - timeZone: zoneId - })); - } catch { - throw new require_NodeRuntime.IllegalArgumentException(`Invalid time zone: ${zoneId}`); - } -}; -/** @internal */ -var zoneMakeOffset$1 = (offset) => { - const zone = Object.create(ProtoTimeZoneOffset); - zone.offset = offset; - return zone; -}; -/** @internal */ -var zoneMakeNamed = /* @__PURE__ */ require_NodeRuntime.liftThrowable(zoneUnsafeMakeNamed$1); -var offsetZoneRegex = /^(?:GMT|[+-])/; -/** @internal */ -var zoneFromString$1 = (zone) => { - if (offsetZoneRegex.test(zone)) { - const offset = parseOffset(zone); - return offset === null ? require_NodeRuntime.none$1() : require_NodeRuntime.some(zoneMakeOffset$1(offset)); - } - return zoneMakeNamed(zone); -}; -/** @internal */ -var zoneToString$1 = (self) => { - if (self._tag === "Offset") return offsetToString(self.offset); - return self.id; -}; -/** @internal */ -var toDateUtc$1 = (self) => new Date(self.epochMillis); -/** @internal */ -var toDate = (self) => { - if (self._tag === "Utc") return new Date(self.epochMillis); - else if (self.zone._tag === "Offset") return new Date(self.epochMillis + self.zone.offset); - else if (self.adjustedEpochMillis !== void 0) return new Date(self.adjustedEpochMillis); - const parts = self.zone.format.formatToParts(self.epochMillis).filter((_) => _.type !== "literal"); - const date = /* @__PURE__ */ new Date(0); - date.setUTCFullYear(Number(parts[2].value), Number(parts[0].value) - 1, Number(parts[1].value)); - date.setUTCHours(Number(parts[3].value), Number(parts[4].value), Number(parts[5].value), Number(parts[6].value)); - self.adjustedEpochMillis = date.getTime(); - return date; -}; -/** @internal */ -var zonedOffset = (self) => { - return toDate(self).getTime() - toEpochMillis$1(self); -}; -var offsetToString = (offset) => { - const abs = Math.abs(offset); - let hours = Math.floor(abs / (3600 * 1e3)); - let minutes = Math.round(abs % (3600 * 1e3) / (60 * 1e3)); - if (minutes === 60) { - hours += 1; - minutes = 0; - } - return `${offset < 0 ? "-" : "+"}${String(hours).padStart(2, "0")}:${String(minutes).padStart(2, "0")}`; -}; -/** @internal */ -var zonedOffsetIso = (self) => offsetToString(zonedOffset(self)); -/** @internal */ -var toEpochMillis$1 = (self) => self.epochMillis; -var setPartsDate = (date, parts) => { - if (parts.year !== void 0) date.setUTCFullYear(parts.year); - if (parts.month !== void 0) date.setUTCMonth(parts.month - 1); - if (parts.day !== void 0) date.setUTCDate(parts.day); - if (parts.weekDay !== void 0) { - const diff = parts.weekDay - date.getUTCDay(); - date.setUTCDate(date.getUTCDate() + diff); - } - if (parts.hours !== void 0) date.setUTCHours(parts.hours); - if (parts.minutes !== void 0) date.setUTCMinutes(parts.minutes); - if (parts.seconds !== void 0) date.setUTCSeconds(parts.seconds); - if (parts.millis !== void 0) date.setUTCMilliseconds(parts.millis); -}; -var constDayMillis = 1440 * 60 * 1e3; -var makeZonedFromAdjusted = (adjustedMillis, zone, disambiguation) => { - if (zone._tag === "Offset") return makeZonedProto(adjustedMillis - zone.offset, zone); - const beforeOffset = calculateNamedOffset(adjustedMillis - constDayMillis, adjustedMillis, zone); - const afterOffset = calculateNamedOffset(adjustedMillis + constDayMillis, adjustedMillis, zone); - if (beforeOffset === afterOffset) return makeZonedProto(adjustedMillis - beforeOffset, zone); - const isForwards = beforeOffset < afterOffset; - const transitionMillis = beforeOffset - afterOffset; - if (isForwards) { - if (calculateNamedOffset(adjustedMillis - afterOffset, adjustedMillis, zone) === afterOffset) return makeZonedProto(adjustedMillis - afterOffset, zone); - const before = makeZonedProto(adjustedMillis - beforeOffset, zone); - if (adjustedMillis !== toDate(before).getTime()) switch (disambiguation) { - case "reject": { - const formatted = new Date(adjustedMillis).toISOString(); - throw new RangeError(`Gap time: ${formatted} does not exist in time zone ${zone.id}`); - } - case "earlier": return makeZonedProto(adjustedMillis - afterOffset, zone); - case "compatible": - case "later": return before; - } - return before; - } - if (calculateNamedOffset(adjustedMillis - beforeOffset, adjustedMillis, zone) === beforeOffset) { - if (disambiguation === "earlier" || disambiguation === "compatible") return makeZonedProto(adjustedMillis - beforeOffset, zone); - if (calculateNamedOffset(adjustedMillis - beforeOffset + transitionMillis, adjustedMillis + transitionMillis, zone) === beforeOffset) return makeZonedProto(adjustedMillis - beforeOffset, zone); - if (disambiguation === "reject") { - const formatted = new Date(adjustedMillis).toISOString(); - throw new RangeError(`Ambiguous time: ${formatted} occurs twice in time zone ${zone.id}`); - } - } - return makeZonedProto(adjustedMillis - afterOffset, zone); -}; -var offsetRegex = /([+-])(\d{2}):(\d{2})$/; -var parseOffset = (offset) => { - const match = offsetRegex.exec(offset); - if (match === null) return null; - const [, sign, hours, minutes] = match; - return (sign === "+" ? 1 : -1) * (Number(hours) * 60 + Number(minutes)) * 60 * 1e3; -}; -var calculateNamedOffset = (utcMillis, adjustedMillis, zone) => { - const offset = zone.format.formatToParts(utcMillis).find((_) => _.type === "timeZoneName")?.value ?? ""; - if (offset === "GMT") return 0; - const result = parseOffset(offset); - if (result === null) return zonedOffset(makeZonedProto(adjustedMillis, zone)); - return result; -}; -/** @internal */ -var formatIso$1 = (self) => toDateUtc$1(self).toISOString(); -/** @internal */ -var formatIsoOffset = (self) => { - const date = toDate(self); - return self._tag === "Utc" ? date.toISOString() : `${date.toISOString().slice(0, -1)}${zonedOffsetIso(self)}`; -}; -/** @internal */ -var formatIsoZoned$1 = (self) => self.zone._tag === "Offset" ? formatIsoOffset(self) : `${formatIsoOffset(self)}[${self.zone.id}]`; -//#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/String.js -/** -* @example -* ```ts -* import * as assert from "node:assert" -* import { pipe, String } from "effect" -* -* assert.deepStrictEqual(pipe('a', String.toUpperCase), 'A') -* ``` -* -* @since 2.0.0 -*/ -var toUpperCase = (self) => self.toUpperCase(); -/** -* @example -* ```ts -* import * as assert from "node:assert" -* import { pipe, String } from "effect" -* -* assert.deepStrictEqual(pipe('A', String.toLowerCase), 'a') -* ``` -* -* @since 2.0.0 -*/ -var toLowerCase = (self) => self.toLowerCase(); -/** -* @example -* ```ts -* import * as assert from "node:assert" -* import { pipe, String } from "effect" -* -* assert.deepStrictEqual(pipe('abc', String.capitalize), 'Abc') -* ``` -* -* @since 2.0.0 -*/ -var capitalize = (self) => { - if (self.length === 0) return self; - return toUpperCase(self[0]) + self.slice(1); -}; -/** -* @example -* ```ts -* import * as assert from "node:assert" -* import { pipe, String } from "effect" -* -* assert.deepStrictEqual(pipe('ABC', String.uncapitalize), 'aBC') -* ``` -* -* @since 2.0.0 -*/ -var uncapitalize = (self) => { - if (self.length === 0) return self; - return toLowerCase(self[0]) + self.slice(1); -}; -/** -* Test whether a `string` is non empty. -* -* @since 2.0.0 -*/ -var isNonEmpty = (self) => self.length > 0; -//#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/DateTime.js -/** -* @since 3.6.0 -* @category guards -*/ -var isDateTime = isDateTime$1; -/** -* @since 3.6.0 -* @category guards -*/ -var isTimeZoneOffset = isTimeZoneOffset$1; -/** -* @since 3.6.0 -* @category guards -*/ -var isTimeZoneNamed = isTimeZoneNamed$1; -/** -* @since 3.6.0 -* @category guards -*/ -var isUtc = isUtc$1; -/** -* @since 3.6.0 -* @category guards -*/ -var isZoned = isZoned$1; -/** -* @since 3.6.0 -* @category instances -*/ -var Equivalence = Equivalence$1; -/** -* Create a `DateTime` from a `Date`. -* -* If the `Date` is invalid, an `IllegalArgumentException` will be thrown. -* -* @since 3.6.0 -* @category constructors -*/ -var unsafeFromDate = unsafeFromDate$1; -/** -* Create a `DateTime` from one of the following: -* -* - A `DateTime` -* - A `Date` instance (invalid dates will throw an `IllegalArgumentException`) -* - The `number` of milliseconds since the Unix epoch -* - An object with the parts of a date -* - A `string` that can be parsed by `Date.parse` -* -* @since 3.6.0 -* @category constructors -* @example -* ```ts -* import { DateTime } from "effect" -* -* // from Date -* DateTime.unsafeMake(new Date()) -* -* // from parts -* DateTime.unsafeMake({ year: 2024 }) -* -* // from string -* DateTime.unsafeMake("2024-01-01") -* ``` -*/ -var unsafeMake = unsafeMake$1; -/** -* Create a `DateTime.Zoned` using `DateTime.unsafeMake` and a time zone. -* -* The input is treated as UTC and then the time zone is attached, unless -* `adjustForTimeZone` is set to `true`. In that case, the input is treated as -* already in the time zone. -* -* When `adjustForTimeZone` is true and ambiguous times occur during DST transitions, -* the `disambiguation` option controls how to resolve the ambiguity: -* - `compatible` (default): Choose earlier time for repeated times, later for gaps -* - `earlier`: Always choose the earlier of two possible times -* - `later`: Always choose the later of two possible times -* - `reject`: Throw an error when ambiguous times are encountered -* -* @since 3.6.0 -* @category constructors -* @example -* ```ts -* import { DateTime } from "effect" -* -* DateTime.unsafeMakeZoned(new Date(), { timeZone: "Europe/London" }) -* ``` -*/ -var unsafeMakeZoned = unsafeMakeZoned$1; -/** -* Create a `DateTime.Zoned` from a string. -* -* It uses the format: `YYYY-MM-DDTHH:mm:ss.sss+HH:MM[Time/Zone]`. -* -* @since 3.6.0 -* @category constructors -*/ -var makeZonedFromString = makeZonedFromString$1; -/** -* Attempt to create a named time zone from a IANA time zone identifier. -* -* If the time zone is invalid, an `IllegalArgumentException` will be thrown. -* -* @since 3.6.0 -* @category time zones -*/ -var zoneUnsafeMakeNamed = zoneUnsafeMakeNamed$1; -/** -* Create a fixed offset time zone. -* -* @since 3.6.0 -* @category time zones -*/ -var zoneMakeOffset = zoneMakeOffset$1; -/** -* Try parse a TimeZone from a string -* -* @since 3.6.0 -* @category time zones -*/ -var zoneFromString = zoneFromString$1; -/** -* Format a `TimeZone` as a string. -* -* @since 3.6.0 -* @category time zones -* @example -* ```ts -* import { DateTime, Effect } from "effect" -* -* // Outputs "+03:00" -* DateTime.zoneToString(DateTime.zoneMakeOffset(3 * 60 * 60 * 1000)) -* -* // Outputs "Europe/London" -* DateTime.zoneToString(DateTime.zoneUnsafeMakeNamed("Europe/London")) -* ``` -*/ -var zoneToString = zoneToString$1; -/** -* Get the UTC `Date` of a `DateTime`. -* -* @since 3.6.0 -* @category conversions -*/ -var toDateUtc = toDateUtc$1; -/** -* Get the milliseconds since the Unix epoch of a `DateTime`. -* -* @since 3.6.0 -* @category conversions -*/ -var toEpochMillis = toEpochMillis$1; -require_NodeRuntime.Tag("effect/DateTime/CurrentTimeZone")(); -/** -* Format a `DateTime` as a UTC ISO string. -* -* @since 3.6.0 -* @category formatting -*/ -var formatIso = formatIso$1; -/** -* Format a `DateTime.Zoned` as a string. -* -* It uses the format: `YYYY-MM-DDTHH:mm:ss.sss+HH:MM[Time/Zone]`. -* -* @since 3.6.0 -* @category formatting -*/ -var formatIsoZoned = formatIsoZoned$1; -//#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/ParseResult.js -/** -* @since 3.10.0 -*/ -/** -* @category model -* @since 3.10.0 -*/ -var Pointer = class { - path; - actual; - issue; - /** - * @since 3.10.0 - */ - _tag = "Pointer"; - constructor(path, actual, issue) { - this.path = path; - this.actual = actual; - this.issue = issue; - } -}; -/** -* Error that occurs when an unexpected key or index is present. -* -* @category model -* @since 3.10.0 -*/ -var Unexpected = class { - actual; - message; - /** - * @since 3.10.0 - */ - _tag = "Unexpected"; - constructor(actual, message) { - this.actual = actual; - this.message = message; - } -}; -/** -* Error that occurs when a required key or index is missing. -* -* @category model -* @since 3.10.0 -*/ -var Missing = class { - ast; - message; - /** - * @since 3.10.0 - */ - _tag = "Missing"; - /** - * @since 3.10.0 - */ - actual = void 0; - constructor(ast, message) { - this.ast = ast; - this.message = message; - } -}; -/** -* Error that contains multiple issues. -* -* @category model -* @since 3.10.0 -*/ -var Composite = class { - ast; - actual; - issues; - output; - /** - * @since 3.10.0 - */ - _tag = "Composite"; - constructor(ast, actual, issues, output) { - this.ast = ast; - this.actual = actual; - this.issues = issues; - this.output = output; - } -}; -/** -* Error that occurs when a refinement has an error. -* -* @category model -* @since 3.10.0 -*/ -var Refinement = class { - ast; - actual; - kind; - issue; - /** - * @since 3.10.0 - */ - _tag = "Refinement"; - constructor(ast, actual, kind, issue) { - this.ast = ast; - this.actual = actual; - this.kind = kind; - this.issue = issue; - } -}; -/** -* Error that occurs when a transformation has an error. -* -* @category model -* @since 3.10.0 -*/ -var Transformation = class { - ast; - actual; - kind; - issue; - /** - * @since 3.10.0 - */ - _tag = "Transformation"; - constructor(ast, actual, kind, issue) { - this.ast = ast; - this.actual = actual; - this.kind = kind; - this.issue = issue; - } -}; -/** -* The `Type` variant of the `ParseIssue` type represents an error that occurs when the `actual` value is not of the expected type. -* The `ast` field specifies the expected type, and the `actual` field contains the value that caused the error. -* -* @category model -* @since 3.10.0 -*/ -var Type = class { - ast; - actual; - message; - /** - * @since 3.10.0 - */ - _tag = "Type"; - constructor(ast, actual, message) { - this.ast = ast; - this.actual = actual; - this.message = message; - } -}; -/** -* The `Forbidden` variant of the `ParseIssue` type represents a forbidden operation, such as when encountering an Effect that is not allowed to execute (e.g., using `runSync`). -* -* @category model -* @since 3.10.0 -*/ -var Forbidden = class { - ast; - actual; - message; - /** - * @since 3.10.0 - */ - _tag = "Forbidden"; - constructor(ast, actual, message) { - this.ast = ast; - this.actual = actual; - this.message = message; - } -}; -/** -* @category type id -* @since 3.10.0 -*/ -var ParseErrorTypeId = /* @__PURE__ */ Symbol.for("effect/Schema/ParseErrorTypeId"); -/** -* @since 3.10.0 -*/ -var ParseError = class extends TaggedError("ParseError") { - /** - * @since 3.10.0 - */ - [ParseErrorTypeId] = ParseErrorTypeId; - get message() { - return this.toString(); - } - /** - * @since 3.10.0 - */ - toString() { - return TreeFormatter.formatIssueSync(this.issue); - } - /** - * @since 3.10.0 - */ - toJSON() { - return { - _id: "ParseError", - message: this.toString() - }; - } - /** - * @since 3.10.0 - */ - [require_NodeRuntime.NodeInspectSymbol]() { - return this.toJSON(); - } -}; -/** -* @category constructors -* @since 3.10.0 -*/ -var parseError = (issue) => new ParseError({ issue }); -/** -* @category constructors -* @since 3.10.0 -*/ -var succeed = require_NodeRuntime.right; -/** -* @category constructors -* @since 3.10.0 -*/ -var fail = require_NodeRuntime.left; -var _try = require_NodeRuntime.try_$1; -/** -* @category constructors -* @since 3.10.0 -*/ -var fromOption = require_NodeRuntime.fromOption; -var isEither = require_NodeRuntime.isEither; -/** -* @category optimisation -* @since 3.10.0 -*/ -var flatMap = /* @__PURE__ */ require_NodeRuntime.dual(2, (self, f) => { - return isEither(self) ? require_NodeRuntime.match$1(self, { - onLeft: require_NodeRuntime.left, - onRight: f - }) : require_NodeRuntime.flatMap(self, f); -}); -/** -* @category optimisation -* @since 3.10.0 -*/ -var map = /* @__PURE__ */ require_NodeRuntime.dual(2, (self, f) => { - return isEither(self) ? require_NodeRuntime.map$2(self, f) : require_NodeRuntime.map(self, f); -}); -/** -* @category optimisation -* @since 3.10.0 -*/ -var mapError = /* @__PURE__ */ require_NodeRuntime.dual(2, (self, f) => { - return isEither(self) ? require_NodeRuntime.mapLeft(self, f) : require_NodeRuntime.mapError(self, f); -}); -/** -* @category optimisation -* @since 3.10.0 -*/ -var mapBoth = /* @__PURE__ */ require_NodeRuntime.dual(2, (self, options) => { - return isEither(self) ? require_NodeRuntime.mapBoth$1(self, { - onLeft: options.onFailure, - onRight: options.onSuccess - }) : require_NodeRuntime.mapBoth(self, options); -}); -/** -* @category optimisation -* @since 3.10.0 -*/ -var orElse = /* @__PURE__ */ require_NodeRuntime.dual(2, (self, f) => { - return isEither(self) ? require_NodeRuntime.match$1(self, { - onLeft: f, - onRight: require_NodeRuntime.right - }) : require_NodeRuntime.catchAll(self, f); -}); -/** @internal */ -var mergeInternalOptions = (options, overrideOptions) => { - if (overrideOptions === void 0 || require_NodeRuntime.isNumber(overrideOptions)) return options; - if (options === void 0) return overrideOptions; +function error(id, message) { return { - ...options, - ...overrideOptions - }; -}; -var getEither = (ast, isDecoding, options) => { - const parser = goMemo(ast, isDecoding); - return (u, overrideOptions) => parser(u, mergeInternalOptions(options, overrideOptions)); -}; -var getSync = (ast, isDecoding, options) => { - const parser = getEither(ast, isDecoding, options); - return (input, overrideOptions) => require_NodeRuntime.getOrThrowWith$1(parser(input, overrideOptions), parseError); -}; -var getEffect = (ast, isDecoding, options) => { - const parser = goMemo(ast, isDecoding); - return (input, overrideOptions) => parser(input, { - ...mergeInternalOptions(options, overrideOptions), - isEffectAllowed: true - }); -}; -/** -* @category decoding -* @since 3.10.0 -*/ -var decodeUnknownEither$1 = (schema, options) => getEither(schema.ast, true, options); -/** -* @category decoding -* @since 3.10.0 -*/ -var decodeUnknown = (schema, options) => getEffect(schema.ast, true, options); -/** -* @category encoding -* @since 3.10.0 -*/ -var encodeUnknown = (schema, options) => getEffect(schema.ast, false, options); -/** -* @throws `ParseError` -* @category validation -* @since 3.10.0 -*/ -var validateSync = (schema, options) => getSync(typeAST(schema.ast), true, options); -var decodeMemoMap = /* @__PURE__ */ require_NodeRuntime.globalValue(/* @__PURE__ */ Symbol.for("effect/ParseResult/decodeMemoMap"), () => /* @__PURE__ */ new WeakMap()); -var encodeMemoMap = /* @__PURE__ */ require_NodeRuntime.globalValue(/* @__PURE__ */ Symbol.for("effect/ParseResult/encodeMemoMap"), () => /* @__PURE__ */ new WeakMap()); -var goMemo = (ast, isDecoding) => { - const memoMap = isDecoding ? decodeMemoMap : encodeMemoMap; - const memo = memoMap.get(ast); - if (memo) return memo; - const raw = go(ast, isDecoding); - const parseOptionsAnnotation = getParseOptionsAnnotation(ast); - const parserWithOptions = require_NodeRuntime.isSome(parseOptionsAnnotation) ? (i, options) => raw(i, mergeInternalOptions(options, parseOptionsAnnotation.value)) : raw; - const decodingFallbackAnnotation = getDecodingFallbackAnnotation(ast); - const parser = isDecoding && require_NodeRuntime.isSome(decodingFallbackAnnotation) ? (i, options) => handleForbidden(orElse(parserWithOptions(i, options), decodingFallbackAnnotation.value), ast, i, options) : parserWithOptions; - memoMap.set(ast, parser); - return parser; -}; -var getConcurrency = (ast) => require_NodeRuntime.getOrUndefined(getConcurrencyAnnotation(ast)); -var getBatching = (ast) => require_NodeRuntime.getOrUndefined(getBatchingAnnotation(ast)); -var go = (ast, isDecoding) => { - switch (ast._tag) { - case "Refinement": if (isDecoding) { - const from = goMemo(ast.from, true); - return (i, options) => { - options = options ?? defaultParseOption; - const allErrors = options?.errors === "all"; - return handleForbidden(flatMap(orElse(from(i, options), (ef) => { - const issue = new Refinement(ast, i, "From", ef); - if (allErrors && hasStableFilter(ast) && isComposite(ef)) return require_NodeRuntime.match(ast.filter(i, options, ast), { - onNone: () => require_NodeRuntime.left(issue), - onSome: (ep) => require_NodeRuntime.left(new Composite(ast, i, [issue, new Refinement(ast, i, "Predicate", ep)])) - }); - return require_NodeRuntime.left(issue); - }), (a) => require_NodeRuntime.match(ast.filter(a, options, ast), { - onNone: () => require_NodeRuntime.right(a), - onSome: (ep) => require_NodeRuntime.left(new Refinement(ast, i, "Predicate", ep)) - })), ast, i, options); - }; - } else { - const from = goMemo(typeAST(ast), true); - const to = goMemo(dropRightRefinement(ast.from), false); - return (i, options) => handleForbidden(flatMap(from(i, options), (a) => to(a, options)), ast, i, options); - } - case "Transformation": { - const transform = getFinalTransformation(ast.transformation, isDecoding); - const from = isDecoding ? goMemo(ast.from, true) : goMemo(ast.to, false); - const to = isDecoding ? goMemo(ast.to, true) : goMemo(ast.from, false); - return (i, options) => handleForbidden(flatMap(mapError(from(i, options), (e) => new Transformation(ast, i, isDecoding ? "Encoded" : "Type", e)), (a) => flatMap(mapError(transform(a, options ?? defaultParseOption, ast, i), (e) => new Transformation(ast, i, "Transformation", e)), (i2) => mapError(to(i2, options), (e) => new Transformation(ast, i, isDecoding ? "Type" : "Encoded", e)))), ast, i, options); - } - case "Declaration": { - const parse = isDecoding ? ast.decodeUnknown(...ast.typeParameters) : ast.encodeUnknown(...ast.typeParameters); - return (i, options) => handleForbidden(parse(i, options ?? defaultParseOption, ast), ast, i, options); - } - case "Literal": return fromRefinement(ast, (u) => u === ast.literal); - case "UniqueSymbol": return fromRefinement(ast, (u) => u === ast.symbol); - case "UndefinedKeyword": return fromRefinement(ast, require_NodeRuntime.isUndefined); - case "NeverKeyword": return fromRefinement(ast, require_NodeRuntime.isNever); - case "UnknownKeyword": - case "AnyKeyword": - case "VoidKeyword": return require_NodeRuntime.right; - case "StringKeyword": return fromRefinement(ast, require_NodeRuntime.isString); - case "NumberKeyword": return fromRefinement(ast, require_NodeRuntime.isNumber); - case "BooleanKeyword": return fromRefinement(ast, require_NodeRuntime.isBoolean); - case "BigIntKeyword": return fromRefinement(ast, require_NodeRuntime.isBigInt); - case "SymbolKeyword": return fromRefinement(ast, require_NodeRuntime.isSymbol); - case "ObjectKeyword": return fromRefinement(ast, require_NodeRuntime.isObject); - case "Enums": return fromRefinement(ast, (u) => ast.enums.some(([_, value]) => value === u)); - case "TemplateLiteral": { - const regex = getTemplateLiteralRegExp(ast); - return fromRefinement(ast, (u) => require_NodeRuntime.isString(u) && regex.test(u)); - } - case "TupleType": { - const elements = ast.elements.map((e) => goMemo(e.type, isDecoding)); - const rest = ast.rest.map((annotatedAST) => goMemo(annotatedAST.type, isDecoding)); - let requiredTypes = ast.elements.filter((e) => !e.isOptional); - if (ast.rest.length > 0) requiredTypes = requiredTypes.concat(ast.rest.slice(1)); - const requiredLen = requiredTypes.length; - const expectedIndexes = ast.elements.length > 0 ? ast.elements.map((_, i) => i).join(" | ") : "never"; - const concurrency = getConcurrency(ast); - const batching = getBatching(ast); - return (input, options) => { - if (!require_NodeRuntime.isArray(input)) return require_NodeRuntime.left(new Type(ast, input)); - const allErrors = options?.errors === "all"; - const es = []; - let stepKey = 0; - const output = []; - const len = input.length; - for (let i = len; i <= requiredLen - 1; i++) { - const e = new Pointer(i, input, new Missing(requiredTypes[i - len])); - if (allErrors) { - es.push([stepKey++, e]); - continue; - } else return require_NodeRuntime.left(new Composite(ast, input, e, output)); - } - if (ast.rest.length === 0) for (let i = ast.elements.length; i <= len - 1; i++) { - const e = new Pointer(i, input, new Unexpected(input[i], `is unexpected, expected: ${expectedIndexes}`)); - if (allErrors) { - es.push([stepKey++, e]); - continue; - } else return require_NodeRuntime.left(new Composite(ast, input, e, output)); - } - let i = 0; - let queue = void 0; - for (; i < elements.length; i++) if (len < i + 1) { - if (ast.elements[i].isOptional) continue; - } else { - const parser = elements[i]; - const te = parser(input[i], options); - if (isEither(te)) { - if (require_NodeRuntime.isLeft(te)) { - const e = new Pointer(i, input, te.left); - if (allErrors) { - es.push([stepKey++, e]); - continue; - } else return require_NodeRuntime.left(new Composite(ast, input, e, sortByIndex(output))); - } - output.push([stepKey++, te.right]); - } else { - const nk = stepKey++; - const index = i; - if (!queue) queue = []; - queue.push(({ es, output }) => require_NodeRuntime.flatMap(require_NodeRuntime.either(te), (t) => { - if (require_NodeRuntime.isLeft(t)) { - const e = new Pointer(index, input, t.left); - if (allErrors) { - es.push([nk, e]); - return require_NodeRuntime._void; - } else return require_NodeRuntime.left(new Composite(ast, input, e, sortByIndex(output))); - } - output.push([nk, t.right]); - return require_NodeRuntime._void; - })); - } - } - if (require_NodeRuntime.isNonEmptyReadonlyArray(rest)) { - const [head, ...tail] = rest; - for (; i < len - tail.length; i++) { - const te = head(input[i], options); - if (isEither(te)) if (require_NodeRuntime.isLeft(te)) { - const e = new Pointer(i, input, te.left); - if (allErrors) { - es.push([stepKey++, e]); - continue; - } else return require_NodeRuntime.left(new Composite(ast, input, e, sortByIndex(output))); - } else output.push([stepKey++, te.right]); - else { - const nk = stepKey++; - const index = i; - if (!queue) queue = []; - queue.push(({ es, output }) => require_NodeRuntime.flatMap(require_NodeRuntime.either(te), (t) => { - if (require_NodeRuntime.isLeft(t)) { - const e = new Pointer(index, input, t.left); - if (allErrors) { - es.push([nk, e]); - return require_NodeRuntime._void; - } else return require_NodeRuntime.left(new Composite(ast, input, e, sortByIndex(output))); - } else { - output.push([nk, t.right]); - return require_NodeRuntime._void; - } - })); - } - } - for (let j = 0; j < tail.length; j++) { - const index = i + j; - if (len < index + 1) continue; - else { - const te = tail[j](input[index], options); - if (isEither(te)) { - if (require_NodeRuntime.isLeft(te)) { - const e = new Pointer(index, input, te.left); - if (allErrors) { - es.push([stepKey++, e]); - continue; - } else return require_NodeRuntime.left(new Composite(ast, input, e, sortByIndex(output))); - } - output.push([stepKey++, te.right]); - } else { - const nk = stepKey++; - if (!queue) queue = []; - queue.push(({ es, output }) => require_NodeRuntime.flatMap(require_NodeRuntime.either(te), (t) => { - if (require_NodeRuntime.isLeft(t)) { - const e = new Pointer(index, input, t.left); - if (allErrors) { - es.push([nk, e]); - return require_NodeRuntime._void; - } else return require_NodeRuntime.left(new Composite(ast, input, e, sortByIndex(output))); - } - output.push([nk, t.right]); - return require_NodeRuntime._void; - })); - } - } - } - } - const computeResult = ({ es, output }) => require_NodeRuntime.isNonEmptyArray(es) ? require_NodeRuntime.left(new Composite(ast, input, sortByIndex(es), sortByIndex(output))) : require_NodeRuntime.right(sortByIndex(output)); - if (queue && queue.length > 0) { - const cqueue = queue; - return require_NodeRuntime.suspend(() => { - const state = { - es: require_NodeRuntime.copy(es), - output: require_NodeRuntime.copy(output) - }; - return require_NodeRuntime.flatMap(require_NodeRuntime.forEach(cqueue, (f) => f(state), { - concurrency, - batching, - discard: true - }), () => computeResult(state)); - }); - } - return computeResult({ - output, - es - }); - }; - } - case "TypeLiteral": { - if (ast.propertySignatures.length === 0 && ast.indexSignatures.length === 0) return fromRefinement(ast, require_NodeRuntime.isNotNullable); - const propertySignatures = []; - const expectedKeysMap = {}; - const expectedKeys = []; - for (const ps of ast.propertySignatures) { - propertySignatures.push([goMemo(ps.type, isDecoding), ps]); - expectedKeysMap[ps.name] = null; - expectedKeys.push(ps.name); - } - const indexSignatures = ast.indexSignatures.map((is) => [ - goMemo(is.parameter, isDecoding), - goMemo(is.type, isDecoding), - is.parameter - ]); - const expectedAST = Union$1.make(ast.indexSignatures.map((is) => is.parameter).concat(expectedKeys.map((key) => require_NodeRuntime.isSymbol(key) ? new UniqueSymbol(key) : new Literal$1(key)))); - const expected = goMemo(expectedAST, isDecoding); - const concurrency = getConcurrency(ast); - const batching = getBatching(ast); - return (input, options) => { - if (!require_NodeRuntime.isRecord(input)) return require_NodeRuntime.left(new Type(ast, input)); - const allErrors = options?.errors === "all"; - const es = []; - let stepKey = 0; - const onExcessPropertyError = options?.onExcessProperty === "error"; - const onExcessPropertyPreserve = options?.onExcessProperty === "preserve"; - const output = {}; - let inputKeys; - if (onExcessPropertyError || onExcessPropertyPreserve) { - inputKeys = Reflect.ownKeys(input); - for (const key of inputKeys) { - const te = expected(key, options); - if (isEither(te) && require_NodeRuntime.isLeft(te)) if (onExcessPropertyError) { - const e = new Pointer(key, input, new Unexpected(input[key], `is unexpected, expected: ${String(expectedAST)}`)); - if (allErrors) { - es.push([stepKey++, e]); - continue; - } else return require_NodeRuntime.left(new Composite(ast, input, e, output)); - } else output[key] = input[key]; - } - } - let queue = void 0; - const isExact = options?.exact === true; - for (let i = 0; i < propertySignatures.length; i++) { - const ps = propertySignatures[i][1]; - const name = ps.name; - const hasKey = Object.prototype.hasOwnProperty.call(input, name); - if (!hasKey) { - if (ps.isOptional) continue; - else if (isExact) { - const e = new Pointer(name, input, new Missing(ps)); - if (allErrors) { - es.push([stepKey++, e]); - continue; - } else return require_NodeRuntime.left(new Composite(ast, input, e, output)); - } - } - const parser = propertySignatures[i][0]; - const te = parser(input[name], options); - if (isEither(te)) { - if (require_NodeRuntime.isLeft(te)) { - const e = new Pointer(name, input, hasKey ? te.left : new Missing(ps)); - if (allErrors) { - es.push([stepKey++, e]); - continue; - } else return require_NodeRuntime.left(new Composite(ast, input, e, output)); - } - output[name] = te.right; - } else { - const nk = stepKey++; - const index = name; - if (!queue) queue = []; - queue.push(({ es, output }) => require_NodeRuntime.flatMap(require_NodeRuntime.either(te), (t) => { - if (require_NodeRuntime.isLeft(t)) { - const e = new Pointer(index, input, hasKey ? t.left : new Missing(ps)); - if (allErrors) { - es.push([nk, e]); - return require_NodeRuntime._void; - } else return require_NodeRuntime.left(new Composite(ast, input, e, output)); - } - output[index] = t.right; - return require_NodeRuntime._void; - })); - } - } - for (let i = 0; i < indexSignatures.length; i++) { - const indexSignature = indexSignatures[i]; - const parameter = indexSignature[0]; - const type = indexSignature[1]; - const keys = getKeysForIndexSignature(input, indexSignature[2]); - for (const key of keys) { - const keu = parameter(key, options); - if (isEither(keu) && require_NodeRuntime.isRight(keu)) { - const vpr = type(input[key], options); - if (isEither(vpr)) { - if (require_NodeRuntime.isLeft(vpr)) { - const e = new Pointer(key, input, vpr.left); - if (allErrors) { - es.push([stepKey++, e]); - continue; - } else return require_NodeRuntime.left(new Composite(ast, input, e, output)); - } else if (!Object.prototype.hasOwnProperty.call(expectedKeysMap, key)) output[key] = vpr.right; - } else { - const nk = stepKey++; - const index = key; - if (!queue) queue = []; - queue.push(({ es, output }) => require_NodeRuntime.flatMap(require_NodeRuntime.either(vpr), (tv) => { - if (require_NodeRuntime.isLeft(tv)) { - const e = new Pointer(index, input, tv.left); - if (allErrors) { - es.push([nk, e]); - return require_NodeRuntime._void; - } else return require_NodeRuntime.left(new Composite(ast, input, e, output)); - } else { - if (!Object.prototype.hasOwnProperty.call(expectedKeysMap, key)) output[key] = tv.right; - return require_NodeRuntime._void; - } - })); - } - } - } - } - const computeResult = ({ es, output }) => { - if (require_NodeRuntime.isNonEmptyArray(es)) return require_NodeRuntime.left(new Composite(ast, input, sortByIndex(es), output)); - if (options?.propertyOrder === "original") { - const keys = inputKeys || Reflect.ownKeys(input); - for (const name of expectedKeys) if (keys.indexOf(name) === -1) keys.push(name); - const out = {}; - for (const key of keys) if (Object.prototype.hasOwnProperty.call(output, key)) out[key] = output[key]; - return require_NodeRuntime.right(out); - } - return require_NodeRuntime.right(output); - }; - if (queue && queue.length > 0) { - const cqueue = queue; - return require_NodeRuntime.suspend(() => { - const state = { - es: require_NodeRuntime.copy(es), - output: Object.assign({}, output) - }; - return require_NodeRuntime.flatMap(require_NodeRuntime.forEach(cqueue, (f) => f(state), { - concurrency, - batching, - discard: true - }), () => computeResult(state)); - }); - } - return computeResult({ - es, - output - }); - }; - } - case "Union": { - const searchTree = getSearchTree(ast.types, isDecoding); - const ownKeys = Reflect.ownKeys(searchTree.keys); - const ownKeysLen = ownKeys.length; - const astTypesLen = ast.types.length; - const map = /* @__PURE__ */ new Map(); - for (let i = 0; i < astTypesLen; i++) map.set(ast.types[i], goMemo(ast.types[i], isDecoding)); - const concurrency = getConcurrency(ast) ?? 1; - const batching = getBatching(ast); - return (input, options) => { - const es = []; - let stepKey = 0; - let candidates = []; - if (ownKeysLen > 0) if (require_NodeRuntime.isRecordOrArray(input)) for (let i = 0; i < ownKeysLen; i++) { - const name = ownKeys[i]; - const buckets = searchTree.keys[name].buckets; - if (Object.prototype.hasOwnProperty.call(input, name)) { - const literal = String(input[name]); - if (Object.prototype.hasOwnProperty.call(buckets, literal)) candidates = candidates.concat(buckets[literal]); - else { - const { candidates, literals } = searchTree.keys[name]; - const literalsUnion = Union$1.make(literals); - const errorAst = candidates.length === astTypesLen ? new TypeLiteral([new PropertySignature(name, literalsUnion, false, true)], []) : Union$1.make(candidates); - es.push([stepKey++, new Composite(errorAst, input, new Pointer(name, input, new Type(literalsUnion, input[name])))]); - } - } else { - const { candidates, literals } = searchTree.keys[name]; - const fakePropertySignature = new PropertySignature(name, Union$1.make(literals), false, true); - const errorAst = candidates.length === astTypesLen ? new TypeLiteral([fakePropertySignature], []) : Union$1.make(candidates); - es.push([stepKey++, new Composite(errorAst, input, new Pointer(name, input, new Missing(fakePropertySignature)))]); - } - } - else { - const errorAst = searchTree.candidates.length === astTypesLen ? ast : Union$1.make(searchTree.candidates); - es.push([stepKey++, new Type(errorAst, input)]); - } - if (searchTree.otherwise.length > 0) candidates = candidates.concat(searchTree.otherwise); - let queue = void 0; - for (let i = 0; i < candidates.length; i++) { - const candidate = candidates[i]; - const pr = map.get(candidate)(input, options); - if (isEither(pr) && (!queue || queue.length === 0)) if (require_NodeRuntime.isRight(pr)) return pr; - else es.push([stepKey++, pr.left]); - else { - const nk = stepKey++; - if (!queue) queue = []; - queue.push((state) => require_NodeRuntime.suspend(() => { - if ("finalResult" in state) return require_NodeRuntime._void; - else return require_NodeRuntime.flatMap(require_NodeRuntime.either(pr), (t) => { - if (require_NodeRuntime.isRight(t)) state.finalResult = t; - else state.es.push([nk, t.left]); - return require_NodeRuntime._void; - }); - })); - } - } - const computeResult = (es) => require_NodeRuntime.isNonEmptyArray(es) ? es.length === 1 && es[0][1]._tag === "Type" ? require_NodeRuntime.left(es[0][1]) : require_NodeRuntime.left(new Composite(ast, input, sortByIndex(es))) : require_NodeRuntime.left(new Type(ast, input)); - if (queue && queue.length > 0) { - const cqueue = queue; - return require_NodeRuntime.suspend(() => { - const state = { es: require_NodeRuntime.copy(es) }; - return require_NodeRuntime.flatMap(require_NodeRuntime.forEach(cqueue, (f) => f(state), { - concurrency, - batching, - discard: true - }), () => { - if ("finalResult" in state) return state.finalResult; - return computeResult(state.es); - }); - }); - } - return computeResult(es); - }; - } - case "Suspend": { - const get = memoizeThunk(() => goMemo(ast.f(), isDecoding)); - return (a, options) => get()(a, options); - } - } -}; -var fromRefinement = (ast, refinement) => (u) => refinement(u) ? require_NodeRuntime.right(u) : require_NodeRuntime.left(new Type(ast, u)); -/** @internal */ -var getLiterals = (ast, isDecoding) => { - switch (ast._tag) { - case "Declaration": { - const annotation = getSurrogateAnnotation(ast); - if (require_NodeRuntime.isSome(annotation)) return getLiterals(annotation.value, isDecoding); - break; - } - case "TypeLiteral": { - const out = []; - for (let i = 0; i < ast.propertySignatures.length; i++) { - const propertySignature = ast.propertySignatures[i]; - const type = isDecoding ? encodedAST(propertySignature.type) : typeAST(propertySignature.type); - if (isLiteral(type) && !propertySignature.isOptional) out.push([propertySignature.name, type]); - } - return out; - } - case "TupleType": { - const out = []; - for (let i = 0; i < ast.elements.length; i++) { - const element = ast.elements[i]; - const type = isDecoding ? encodedAST(element.type) : typeAST(element.type); - if (isLiteral(type) && !element.isOptional) out.push([i, type]); - } - return out; + jsonrpc: "2.0", + id, + error: { + code: -32e3, + message } - case "Refinement": return getLiterals(ast.from, isDecoding); - case "Suspend": return getLiterals(ast.f(), isDecoding); - case "Transformation": return getLiterals(isDecoding ? ast.from : ast.to, isDecoding); - } - return []; -}; -/** -* The purpose of the algorithm is to narrow down the pool of possible -* candidates for decoding as much as possible. -* -* This function separates the schemas into two groups, `keys` and `otherwise`: -* -* - `keys`: the schema has at least one property with a literal value -* - `otherwise`: the schema has no properties with a literal value -* -* If a schema has at least one property with a literal value, so it ends up in -* `keys`, first a namespace is created for the name of the property containing -* the literal, and then within this namespace a "bucket" is created for the -* literal value in which to store all the schemas that have the same property -* and literal value. -* -* @internal -*/ -var getSearchTree = (members, isDecoding) => { - const keys = {}; - const otherwise = []; - const candidates = []; - for (let i = 0; i < members.length; i++) { - const member = members[i]; - const tags = getLiterals(member, isDecoding); - if (tags.length > 0) { - candidates.push(member); - for (let j = 0; j < tags.length; j++) { - const [key, literal] = tags[j]; - const hash = String(literal.literal); - keys[key] = keys[key] || { - buckets: {}, - literals: [], - candidates: [] - }; - const buckets = keys[key].buckets; - if (Object.prototype.hasOwnProperty.call(buckets, hash)) { - if (j < tags.length - 1) continue; - buckets[hash].push(member); - keys[key].literals.push(literal); - keys[key].candidates.push(member); - } else { - buckets[hash] = [member]; - keys[key].literals.push(literal); - keys[key].candidates.push(member); - break; - } - } - } else otherwise.push(member); - } - return { - keys, - otherwise, - candidates }; -}; -var dropRightRefinement = (ast) => isRefinement$1(ast) ? dropRightRefinement(ast.from) : ast; -var handleForbidden = (effect, ast, actual, options) => { - if (options?.isEffectAllowed === true) return effect; - if (isEither(effect)) return effect; - const scheduler = new require_NodeRuntime.SyncScheduler(); - const fiber = require_NodeRuntime.runFork(effect, { scheduler }); - scheduler.flush(); - const exit = fiber.unsafePoll(); - if (exit) { - if (require_NodeRuntime.isSuccess(exit)) return require_NodeRuntime.right(exit.value); - const cause = exit.cause; - if (require_NodeRuntime.isFailType(cause)) return require_NodeRuntime.left(cause.error); - return require_NodeRuntime.left(new Forbidden(ast, actual, require_NodeRuntime.pretty(cause))); - } - return require_NodeRuntime.left(new Forbidden(ast, actual, "cannot be be resolved synchronously, this is caused by using runSync on an effect that performs async work")); -}; -var compare = ([a], [b]) => a > b ? 1 : a < b ? -1 : 0; -function sortByIndex(es) { - return es.sort(compare).map((t) => t[1]); } -/** @internal */ -var getFinalTransformation = (transformation, isDecoding) => { - switch (transformation._tag) { - case "FinalTransformation": return isDecoding ? transformation.decode : transformation.encode; - case "ComposeTransformation": return require_NodeRuntime.right; - case "TypeLiteralTransformation": return (input) => { - let out = require_NodeRuntime.right(input); - for (const pst of transformation.propertySignatureTransformations) { - const [from, to] = isDecoding ? [pst.from, pst.to] : [pst.to, pst.from]; - const transformation = isDecoding ? pst.decode : pst.encode; - const f = (input) => { - const o = transformation(Object.prototype.hasOwnProperty.call(input, from) ? require_NodeRuntime.some(input[from]) : require_NodeRuntime.none$1()); - delete input[from]; - if (require_NodeRuntime.isSome(o)) input[to] = o.value; - return input; - }; - out = map(out, f); - } - return out; - }; - } -}; -var makeTree = (value, forest = []) => ({ - value, - forest -}); -/** -* @category formatting -* @since 3.10.0 -*/ -var TreeFormatter = { - formatIssue: (issue) => map(formatTree(issue), drawTree), - formatIssueSync: (issue) => { - const e = TreeFormatter.formatIssue(issue); - return isEither(e) ? require_NodeRuntime.getOrThrow(e) : require_NodeRuntime.runSync(e); - }, - formatError: (error) => TreeFormatter.formatIssue(error.issue), - formatErrorSync: (error) => TreeFormatter.formatIssueSync(error.issue) -}; -var drawTree = (tree) => tree.value + draw("\n", tree.forest); -var draw = (indentation, forest) => { - let r = ""; - const len = forest.length; - let tree; - for (let i = 0; i < len; i++) { - tree = forest[i]; - const isLast = i === len - 1; - r += indentation + (isLast ? "└" : "├") + "─ " + tree.value; - r += draw(indentation + (len > 1 && !isLast ? "│ " : " "), tree.forest); - } - return r; -}; -var formatTransformationKind = (kind) => { - switch (kind) { - case "Encoded": return "Encoded side transformation failure"; - case "Transformation": return "Transformation process failure"; - case "Type": return "Type side transformation failure"; - } -}; -var formatRefinementKind = (kind) => { - switch (kind) { - case "From": return "From side refinement failure"; - case "Predicate": return "Predicate refinement failure"; - } -}; -var getAnnotated = (issue) => "ast" in issue ? require_NodeRuntime.some(issue.ast) : require_NodeRuntime.none$1(); -var Either_void = /* @__PURE__ */ require_NodeRuntime.right(void 0); -var getCurrentMessage = (issue) => getAnnotated(issue).pipe(require_NodeRuntime.flatMap$2(getMessageAnnotation), require_NodeRuntime.match({ - onNone: () => Either_void, - onSome: (messageAnnotation) => { - const union = messageAnnotation(issue); - if (require_NodeRuntime.isString(union)) return require_NodeRuntime.right({ - message: union, - override: false - }); - if (require_NodeRuntime.isEffect(union)) return require_NodeRuntime.map(union, (message) => ({ - message, - override: false - })); - if (require_NodeRuntime.isString(union.message)) return require_NodeRuntime.right({ - message: union.message, - override: union.override - }); - return require_NodeRuntime.map(union.message, (message) => ({ - message, - override: union.override - })); - } -})); -var createParseIssueGuard = (tag) => (issue) => issue._tag === tag; -/** -* Returns `true` if the value is a `Composite`. -* -* @category guards -* @since 3.10.0 -*/ -var isComposite = /* @__PURE__ */ createParseIssueGuard("Composite"); -var isRefinement = /* @__PURE__ */ createParseIssueGuard("Refinement"); -var isTransformation = /* @__PURE__ */ createParseIssueGuard("Transformation"); -var getMessage = (issue) => flatMap(getCurrentMessage(issue), (currentMessage) => { - if (currentMessage !== void 0) return !currentMessage.override && (isComposite(issue) || isRefinement(issue) && issue.kind === "From" || isTransformation(issue) && issue.kind !== "Transformation") ? isTransformation(issue) || isRefinement(issue) ? getMessage(issue.issue) : Either_void : require_NodeRuntime.right(currentMessage.message); - return Either_void; -}); -var getParseIssueTitleAnnotation = (issue) => getAnnotated(issue).pipe(require_NodeRuntime.flatMap$2(getParseIssueTitleAnnotation$1), require_NodeRuntime.flatMapNullable((annotation) => annotation(issue)), require_NodeRuntime.getOrUndefined); -/** @internal */ -function getRefinementExpected(ast) { - return getDescriptionAnnotation(ast).pipe(require_NodeRuntime.orElse(() => getTitleAnnotation(ast)), require_NodeRuntime.orElse(() => getAutoTitleAnnotation(ast)), require_NodeRuntime.orElse(() => getIdentifierAnnotation(ast)), require_NodeRuntime.getOrElse(() => `{ ${ast.from} | filter }`)); -} -function getDefaultTypeMessage(issue) { - if (issue.message !== void 0) return issue.message; - return `Expected ${isRefinement$1(issue.ast) ? getRefinementExpected(issue.ast) : String(issue.ast)}, actual ${require_NodeRuntime.formatUnknown(issue.actual)}`; -} -var formatTypeMessage = (issue) => map(getMessage(issue), (message) => message ?? getParseIssueTitleAnnotation(issue) ?? getDefaultTypeMessage(issue)); -var getParseIssueTitle = (issue) => getParseIssueTitleAnnotation(issue) ?? String(issue.ast); -var formatForbiddenMessage = (issue) => issue.message ?? "is forbidden"; -var formatUnexpectedMessage = (issue) => issue.message ?? "is unexpected"; -var formatMissingMessage = (issue) => { - const missingMessageAnnotation = getMissingMessageAnnotation(issue.ast); - if (require_NodeRuntime.isSome(missingMessageAnnotation)) { - const annotation = missingMessageAnnotation.value(); - return require_NodeRuntime.isString(annotation) ? require_NodeRuntime.right(annotation) : annotation; - } - return require_NodeRuntime.right(issue.message ?? "is missing"); -}; -var formatTree = (issue) => { - switch (issue._tag) { - case "Type": return map(formatTypeMessage(issue), makeTree); - case "Forbidden": return require_NodeRuntime.right(makeTree(getParseIssueTitle(issue), [makeTree(formatForbiddenMessage(issue))])); - case "Unexpected": return require_NodeRuntime.right(makeTree(formatUnexpectedMessage(issue))); - case "Missing": return map(formatMissingMessage(issue), makeTree); - case "Transformation": return flatMap(getMessage(issue), (message) => { - if (message !== void 0) return require_NodeRuntime.right(makeTree(message)); - return map(formatTree(issue.issue), (tree) => makeTree(getParseIssueTitle(issue), [makeTree(formatTransformationKind(issue.kind), [tree])])); - }); - case "Refinement": return flatMap(getMessage(issue), (message) => { - if (message !== void 0) return require_NodeRuntime.right(makeTree(message)); - return map(formatTree(issue.issue), (tree) => makeTree(getParseIssueTitle(issue), [makeTree(formatRefinementKind(issue.kind), [tree])])); - }); - case "Pointer": return map(formatTree(issue.issue), (tree) => makeTree(formatPath(issue.path), [tree])); - case "Composite": return flatMap(getMessage(issue), (message) => { - if (message !== void 0) return require_NodeRuntime.right(makeTree(message)); - const parseIssueTitle = getParseIssueTitle(issue); - return isNonEmpty$1(issue.issues) ? map(require_NodeRuntime.forEach(issue.issues, formatTree), (forest) => makeTree(parseIssueTitle, forest)) : map(formatTree(issue.issues), (tree) => makeTree(parseIssueTitle, [tree])); - }); - } -}; -//#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Struct.js -/** -* Create a new object by picking properties of an existing object. -* -* @example -* ```ts -* import * as assert from "node:assert" -* import { pipe, Struct } from "effect" -* -* assert.deepStrictEqual(pipe({ a: "a", b: 1, c: true }, Struct.pick("a", "b")), { a: "a", b: 1 }) -* assert.deepStrictEqual(Struct.pick({ a: "a", b: 1, c: true }, "a", "b"), { a: "a", b: 1 }) -* ``` -* -* @since 2.0.0 -*/ -var pick = /* @__PURE__ */ require_NodeRuntime.dual((args) => require_NodeRuntime.isObject(args[0]), (s, ...keys) => { - const out = {}; - for (const k of keys) if (k in s) out[k] = s[k]; - return out; -}); -/** -* Create a new object by omitting properties of an existing object. -* -* @example -* ```ts -* import * as assert from "node:assert" -* import { pipe, Struct } from "effect" -* -* assert.deepStrictEqual(pipe({ a: "a", b: 1, c: true }, Struct.omit("c")), { a: "a", b: 1 }) -* assert.deepStrictEqual(Struct.omit({ a: "a", b: 1, c: true }, "c"), { a: "a", b: 1 }) -* ``` -* -* @since 2.0.0 -*/ -var omit = /* @__PURE__ */ require_NodeRuntime.dual((args) => require_NodeRuntime.isObject(args[0]), (s, ...keys) => { - const out = { ...s }; - for (const k of keys) delete out[k]; - return out; -}); -//#endregion -//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Schema.js -/** -* @since 3.10.0 -*/ -/** -* @since 3.10.0 -* @category symbol -*/ -var TypeId = /* @__PURE__ */ Symbol.for("effect/Schema"); -/** -* @category constructors -* @since 3.10.0 -*/ -function make(ast) { - return class SchemaClass { - [TypeId] = variance; - static ast = ast; - static annotations(annotations) { - return make(mergeSchemaAnnotations(this.ast, annotations)); - } - static pipe() { - return require_NodeRuntime.pipeArguments(this, arguments); - } - static toString() { - return String(ast); - } - static Type; - static Encoded; - static Context; - static [TypeId] = variance; - }; +function reply(socket, message) { + if (!socket.destroyed) socket.write(`${JSON.stringify(message)}\n`); } -var variance = { - /* c8 ignore next */ - _A: (_) => _, - /* c8 ignore next */ - _I: (_) => _, - /* c8 ignore next */ - _R: (_) => _ -}; -var builtInAnnotations = { - typeConstructor: TypeConstructorAnnotationId, - schemaId: SchemaIdAnnotationId, - message: MessageAnnotationId, - missingMessage: MissingMessageAnnotationId, - identifier: IdentifierAnnotationId, - title: TitleAnnotationId, - description: DescriptionAnnotationId, - examples: ExamplesAnnotationId, - default: DefaultAnnotationId, - documentation: DocumentationAnnotationId, - jsonSchema: JSONSchemaAnnotationId, - arbitrary: ArbitraryAnnotationId, - pretty: PrettyAnnotationId, - equivalence: EquivalenceAnnotationId, - concurrency: ConcurrencyAnnotationId, - batching: BatchingAnnotationId, - parseIssueTitle: ParseIssueTitleAnnotationId, - parseOptions: ParseOptionsAnnotationId, - decodingFallback: DecodingFallbackAnnotationId -}; -var toASTAnnotations = (annotations) => { - if (!annotations) return {}; - const out = { ...annotations }; - for (const key in builtInAnnotations) if (key in annotations) { - const id = builtInAnnotations[key]; - out[id] = annotations[key]; - delete out[key]; +function release(socket) { + const client = clients.get(socket); + if (!client) return; + clients.delete(socket); + for (const [id, request] of pending) { + if (request.socket !== socket) continue; + clearTimeout(request.timer); + pending.delete(id); } - return out; -}; -var mergeSchemaAnnotations = (ast, annotations$3) => annotations(ast, toASTAnnotations(annotations$3)); -/** -* @category formatting -* @since 3.10.0 -*/ -var format = (schema) => String(schema.ast); -/** -* @category decoding -* @since 3.10.0 -*/ -var decodeUnknownEither = (schema, options) => { - const decodeUnknownEither = decodeUnknownEither$1(schema, options); - return (u, overrideOptions) => require_NodeRuntime.mapLeft(decodeUnknownEither(u, overrideOptions), parseError); -}; -/** -* Tests if a value is a `Schema`. -* -* @category guards -* @since 3.10.0 -*/ -var isSchema = (u) => require_NodeRuntime.hasProperty(u, TypeId) && require_NodeRuntime.isObject(u[TypeId]); -function getDefaultLiteralAST(literals) { - return isMembers(literals) ? Union$1.make(mapMembers(literals, (literal) => new Literal$1(literal))) : new Literal$1(literals[0]); -} -function makeLiteralClass(literals, ast = getDefaultLiteralAST(literals)) { - return class LiteralClass extends make(ast) { - static annotations(annotations) { - return makeLiteralClass(this.literals, mergeSchemaAnnotations(this.ast, annotations)); - } - static literals = [...literals]; - }; -} -function Literal(...literals) { - return require_NodeRuntime.isNonEmptyReadonlyArray(literals) ? makeLiteralClass(literals) : Never; + if (client.authenticated && extensionProtocol === "ready") native({ + jsonrpc: "2.0", + method: "internal.releaseClient", + params: { session_id: client.session } + }); } -var declareConstructor = (typeParameters, options, annotations) => makeDeclareClass(typeParameters, new Declaration(typeParameters.map((tp) => tp.ast), (...typeParameters) => options.decode(...typeParameters.map(make)), (...typeParameters) => options.encode(...typeParameters.map(make)), toASTAnnotations(annotations))); -var declarePrimitive = (is, annotations) => { - const decodeUnknown = () => (input, _, ast) => is(input) ? succeed(input) : fail(new Type(ast, input)); - return makeDeclareClass([], new Declaration([], decodeUnknown, decodeUnknown, toASTAnnotations(annotations))); -}; -function makeDeclareClass(typeParameters, ast) { - return class DeclareClass extends make(ast) { - static annotations(annotations) { - return makeDeclareClass(this.typeParameters, mergeSchemaAnnotations(this.ast, annotations)); +function handleNative(message) { + if (!require_rpc.isJsonRpcRequest(message)) { + if (message.id === protocolRequestId) { + if (extensionProtocol !== "checking") return; + clearTimeout(protocolTimer); + extensionProtocol = !message.error && typeof message.result === "object" && message.result !== null && "protocolVersion" in message.result && message.result.protocolVersion === 2 ? "ready" : "unsupported"; + return; } - static typeParameters = [...typeParameters]; - }; -} -/** -* The constraint `R extends Schema.Context` enforces dependencies solely from `typeParameters`. -* This ensures that when you call `Schema.to` or `Schema.from`, you receive a schema with a `never` context. -* -* @category constructors -* @since 3.10.0 -*/ -var declare = function() { - if (Array.isArray(arguments[0])) { - const typeParameters = arguments[0]; - const options = arguments[1]; - const annotations = arguments[2]; - return declareConstructor(typeParameters, options, annotations); + const request = typeof message.id === "number" ? pending.get(message.id) : void 0; + if (!request) return; + pending.delete(Number(message.id)); + clearTimeout(request.timer); + if (request.private) { + const ok = !message.error && typeof message.result === "object" && message.result !== null && "status" in message.result && message.result.status === "filled"; + reply(request.socket, result(request.id, { + status: ok ? "filled" : "not-filled-or-unknown", + retry: false + })); + } else reply(request.socket, message.error ? error(request.id, message.error.message) : result(request.id, message.result)); + return; } - const is = arguments[0]; - const annotations = arguments[1]; - return declarePrimitive(is, annotations); -}; -/** -* @category schema id -* @since 3.10.0 -*/ -var InstanceOfSchemaId = /* @__PURE__ */ Symbol.for("effect/SchemaId/InstanceOf"); -/** -* @category constructors -* @since 3.10.0 -*/ -var instanceOf = (constructor, annotations) => declare((u) => u instanceof constructor, { - title: constructor.name, - description: `an instance of ${constructor.name}`, - pretty: () => String, - schemaId: InstanceOfSchemaId, - [InstanceOfSchemaId]: { constructor }, - ...annotations -}); -/** -* @category primitives -* @since 3.10.0 -*/ -var Undefined = class extends make(undefinedKeyword) {}; -/** -* @category primitives -* @since 3.10.0 -*/ -var Null = class extends make($null) {}; -/** -* @category primitives -* @since 3.10.0 -*/ -var Never = class extends make(neverKeyword) {}; -/** -* @category primitives -* @since 3.10.0 -*/ -var Unknown = class extends make(unknownKeyword) {}; -/** -* @category primitives -* @since 3.10.0 -*/ -var BigIntFromSelf = class extends make(bigIntKeyword) {}; -/** -* @category primitives -* @since 3.10.0 -*/ -var SymbolFromSelf = class extends make(symbolKeyword) {}; -/** @ignore */ -var String$ = class extends make(stringKeyword) {}; -/** @ignore */ -var Number$ = class extends make(numberKeyword) {}; -/** @ignore */ -var Boolean$ = class extends make(booleanKeyword) {}; -var getDefaultUnionAST = (members) => Union$1.make(members.map((m) => m.ast)); -function makeUnionClass(members, ast = getDefaultUnionAST(members)) { - return class UnionClass extends make(ast) { - static annotations(annotations) { - return makeUnionClass(this.members, mergeSchemaAnnotations(this.ast, annotations)); - } - static members = [...members]; - }; -} -function Union(...members) { - return isMembers(members) ? makeUnionClass(members) : require_NodeRuntime.isNonEmptyReadonlyArray(members) ? members[0] : Never; -} -/** -* @category combinators -* @since 3.10.0 -*/ -var UndefinedOr = (self) => Union(self, Undefined); -/** -* @since 3.10.0 -*/ -var element = (self) => new ElementImpl(new OptionalType(self.ast, false), self); -var ElementImpl = class ElementImpl { - ast; - from; - [TypeId]; - _Token; - constructor(ast, from) { - this.ast = ast; - this.from = from; + if (message.id != null) { + if (message.method === "ping") native(result(message.id, "pong")); + else if (message.method === "getHostInfo") native(result(message.id, { + name: "opzero-chrome-native-host", + version: "0.2.0", + protocolVersion: 2, + extensionProtocol, + epoch, + pid: node_process.default.pid, + transport: useTcp ? "tcp" : "unix", + endpoint: useTcp ? `127.0.0.1:${port}` : socketPath + })); + else native(error(message.id, "Unsupported native host method")); + return; } - annotations(annotations) { - return new ElementImpl(new OptionalType(this.ast.type, this.ast.isOptional, { - ...this.ast.annotations, - ...toASTAnnotations(annotations) - }), this.from); + if (extensionProtocol !== "ready") return; + const params = typeof message.params === "object" && message.params !== null ? message.params : {}; + const session = "session_id" in params ? params.session_id : void 0; + for (const [socket, client] of clients) { + if (!client.authenticated) continue; + if (typeof session === "string" ? client.session === session : message.method === "onControlStopped" || message.method === "onDownloadChange" && client.profileEvents) reply(socket, message); } - toString() { - return `${this.ast.type}${this.ast.isOptional ? "?" : ""}`; + if (message.method === "onControlStopped") for (const socket of clients.keys()) { + release(socket); + socket.end(); } -}; -var getDefaultTupleTypeAST = (elements, rest) => new TupleType(elements.map((el) => isSchema(el) ? new OptionalType(el.ast, false) : el.ast), rest.map((el) => isSchema(el) ? new Type$1(el.ast) : el.ast), true); -function makeTupleTypeClass(elements, rest, ast = getDefaultTupleTypeAST(elements, rest)) { - return class TupleTypeClass extends make(ast) { - static annotations(annotations) { - return makeTupleTypeClass(this.elements, this.rest, mergeSchemaAnnotations(this.ast, annotations)); - } - static elements = [...elements]; - static rest = [...rest]; - }; -} -function Tuple(...args) { - return Array.isArray(args[0]) ? makeTupleTypeClass(args[0], args.slice(1)) : makeTupleTypeClass(args, []); } -function makeArrayClass(value, ast) { - return class ArrayClass extends makeTupleTypeClass([], [value], ast) { - static annotations(annotations) { - return makeArrayClass(this.value, mergeSchemaAnnotations(this.ast, annotations)); - } - static value = value; - }; -} -var Array$ = (value) => makeArrayClass(value); -var formatPropertySignatureToken = (isOptional) => isOptional ? "\"?:\"" : "\":\""; -/** -* @category PropertySignature -* @since 3.10.0 -*/ -var PropertySignatureDeclaration = class extends OptionalType { - isReadonly; - defaultValue; - /** - * @since 3.10.0 - */ - _tag = "PropertySignatureDeclaration"; - constructor(type, isOptional, isReadonly, annotations, defaultValue) { - super(type, isOptional, annotations); - this.isReadonly = isReadonly; - this.defaultValue = defaultValue; - } - /** - * @since 3.10.0 - */ - toString() { - const token = formatPropertySignatureToken(this.isOptional); - const type = String(this.type); - return `PropertySignature<${token}, ${type}, never, ${token}, ${type}>`; - } -}; -/** -* @category PropertySignature -* @since 3.10.0 -*/ -var ToPropertySignature = class extends OptionalType { - isReadonly; - defaultValue; - constructor(type, isOptional, isReadonly, annotations, defaultValue) { - super(type, isOptional, annotations); - this.isReadonly = isReadonly; - this.defaultValue = defaultValue; - } -}; -var formatPropertyKey = (p) => { - if (p === void 0) return "never"; - if (require_NodeRuntime.isString(p)) return JSON.stringify(p); - return String(p); -}; -/** -* @category PropertySignature -* @since 3.10.0 -*/ -var PropertySignatureTransformation = class { - from; - to; - decode; - encode; - /** - * @since 3.10.0 - */ - _tag = "PropertySignatureTransformation"; - constructor(from, to, decode, encode) { - this.from = from; - this.to = to; - this.decode = decode; - this.encode = encode; - } - /** - * @since 3.10.0 - */ - toString() { - return `PropertySignature<${formatPropertySignatureToken(this.to.isOptional)}, ${this.to.type}, ${formatPropertyKey(this.from.fromKey)}, ${formatPropertySignatureToken(this.from.isOptional)}, ${this.from.type}>`; - } -}; -var mergeSignatureAnnotations = (ast, annotations) => { - switch (ast._tag) { - case "PropertySignatureDeclaration": return new PropertySignatureDeclaration(ast.type, ast.isOptional, ast.isReadonly, { - ...ast.annotations, - ...annotations - }, ast.defaultValue); - case "PropertySignatureTransformation": return new PropertySignatureTransformation(ast.from, new ToPropertySignature(ast.to.type, ast.to.isOptional, ast.to.isReadonly, { - ...ast.to.annotations, - ...annotations - }, ast.to.defaultValue), ast.decode, ast.encode); - } -}; -/** -* @since 3.10.0 -* @category symbol -*/ -var PropertySignatureTypeId = /* @__PURE__ */ Symbol.for("effect/PropertySignature"); -/** -* @since 3.10.0 -* @category guards -*/ -var isPropertySignature = (u) => require_NodeRuntime.hasProperty(u, PropertySignatureTypeId); -var PropertySignatureImpl = class PropertySignatureImpl { - ast; - [TypeId]; - [PropertySignatureTypeId] = null; - _TypeToken; - _Key; - _EncodedToken; - _HasDefault; - constructor(ast) { - this.ast = ast; - } - pipe() { - return require_NodeRuntime.pipeArguments(this, arguments); - } - annotations(annotations) { - return new PropertySignatureImpl(mergeSignatureAnnotations(this.ast, toASTAnnotations(annotations))); - } - toString() { - return String(this.ast); - } -}; -/** -* @category PropertySignature -* @since 3.10.0 -*/ -var makePropertySignature = (ast) => new PropertySignatureImpl(ast); -var PropertySignatureWithFromImpl = class PropertySignatureWithFromImpl extends PropertySignatureImpl { - from; - constructor(ast, from) { - super(ast); - this.from = from; - } - annotations(annotations) { - return new PropertySignatureWithFromImpl(mergeSignatureAnnotations(this.ast, toASTAnnotations(annotations)), this.from); - } -}; -/** -* Lifts a `Schema` into a `PropertySignature`. -* -* @category PropertySignature -* @since 3.10.0 -*/ -var propertySignature = (self) => new PropertySignatureWithFromImpl(new PropertySignatureDeclaration(self.ast, false, true, {}, void 0), self); -/** -* Enhances a property signature with a default constructor value. -* -* @category PropertySignature -* @since 3.10.0 -*/ -var withConstructorDefault = /* @__PURE__ */ require_NodeRuntime.dual(2, (self, defaultValue) => { - const ast = self.ast; - switch (ast._tag) { - case "PropertySignatureDeclaration": return makePropertySignature(new PropertySignatureDeclaration(ast.type, ast.isOptional, ast.isReadonly, ast.annotations, defaultValue)); - case "PropertySignatureTransformation": return makePropertySignature(new PropertySignatureTransformation(ast.from, new ToPropertySignature(ast.to.type, ast.to.isOptional, ast.to.isReadonly, ast.to.annotations, defaultValue), ast.decode, ast.encode)); - } -}); -/** -* @category PropertySignature -* @since 3.10.0 -*/ -var optional = (self) => { - return new PropertySignatureWithFromImpl(new PropertySignatureDeclaration(self.ast === undefinedKeyword || self.ast === neverKeyword ? undefinedKeyword : UndefinedOr(self).ast, true, true, {}, void 0), self); -}; -var preserveMissingMessageAnnotation = /* @__PURE__ */ pickAnnotations([MissingMessageAnnotationId]); -var getDefaultTypeLiteralAST = (fields, records) => { - const ownKeys = Reflect.ownKeys(fields); - const pss = []; - if (ownKeys.length > 0) { - const from = []; - const to = []; - const transformations = []; - for (let i = 0; i < ownKeys.length; i++) { - const key = ownKeys[i]; - const field = fields[key]; - if (isPropertySignature(field)) { - const ast = field.ast; - switch (ast._tag) { - case "PropertySignatureDeclaration": { - const type = ast.type; - const isOptional = ast.isOptional; - const toAnnotations = ast.annotations; - from.push(new PropertySignature(key, type, isOptional, true, preserveMissingMessageAnnotation(ast))); - to.push(new PropertySignature(key, typeAST(type), isOptional, true, toAnnotations)); - pss.push(new PropertySignature(key, type, isOptional, true, toAnnotations)); - break; - } - case "PropertySignatureTransformation": { - const fromKey = ast.from.fromKey ?? key; - from.push(new PropertySignature(fromKey, ast.from.type, ast.from.isOptional, true, ast.from.annotations)); - to.push(new PropertySignature(key, ast.to.type, ast.to.isOptional, true, ast.to.annotations)); - transformations.push(new PropertySignatureTransformation$1(fromKey, key, ast.decode, ast.encode)); - break; - } - } - } else { - from.push(new PropertySignature(key, field.ast, false, true)); - to.push(new PropertySignature(key, typeAST(field.ast), false, true)); - pss.push(new PropertySignature(key, field.ast, false, true)); - } - } - if (require_NodeRuntime.isNonEmptyReadonlyArray(transformations)) { - const issFrom = []; - const issTo = []; - for (const r of records) { - const { indexSignatures, propertySignatures } = record(r.key.ast, r.value.ast); - propertySignatures.forEach((ps) => { - from.push(ps); - to.push(new PropertySignature(ps.name, typeAST(ps.type), ps.isOptional, ps.isReadonly, ps.annotations)); - }); - indexSignatures.forEach((is) => { - issFrom.push(is); - issTo.push(new IndexSignature(is.parameter, typeAST(is.type), is.isReadonly)); - }); - } - return new Transformation$1(new TypeLiteral(from, issFrom, { [AutoTitleAnnotationId]: "Struct (Encoded side)" }), new TypeLiteral(to, issTo, { [AutoTitleAnnotationId]: "Struct (Type side)" }), new TypeLiteralTransformation(transformations)); - } +function handleClient(socket, message) { + const client = clients.get(socket); + if (!client) return; + if (!require_rpc.isJsonRpcRequest(message) || typeof message.id !== "number" && typeof message.id !== "string") { + reply(socket, error(null, "Expected JSON-RPC request with a non-null id")); + return; } - const iss = []; - for (const r of records) { - const { indexSignatures, propertySignatures } = record(r.key.ast, r.value.ast); - propertySignatures.forEach((ps) => pss.push(ps)); - indexSignatures.forEach((is) => iss.push(is)); + const id = JSON.stringify(message.id); + if (client.ids.has(id)) { + reply(socket, error(message.id, "Duplicate id; action not replayed")); + return; } - return new TypeLiteral(pss, iss); -}; -var lazilyMergeDefaults = (fields, out) => { - const ownKeys = Reflect.ownKeys(fields); - for (const key of ownKeys) { - const field = fields[key]; - if (out[key] === void 0 && isPropertySignature(field)) { - const ast = field.ast; - const defaultValue = ast._tag === "PropertySignatureDeclaration" ? ast.defaultValue : ast.to.defaultValue; - if (defaultValue !== void 0) out[key] = defaultValue(); - } + if (client.ids.size >= 1e4) { + reply(socket, error(message.id, "Connection request limit reached; no action dispatched; connection revoked")); + release(socket); + socket.end(); + return; } - return out; -}; -function makeTypeLiteralClass(fields, records, ast = getDefaultTypeLiteralAST(fields, records)) { - return class TypeLiteralClass extends make(ast) { - static annotations(annotations) { - return makeTypeLiteralClass(this.fields, this.records, mergeSchemaAnnotations(this.ast, annotations)); - } - static fields = { ...fields }; - static records = [...records]; - static make = (props, options) => { - const propsWithDefaults = lazilyMergeDefaults(fields, { ...props }); - return getDisableValidationMakeOption(options) ? propsWithDefaults : validateSync(this)(propsWithDefaults); - }; - static pick(...keys) { - return Struct(pick(fields, ...keys)); - } - static omit(...keys) { - return Struct(omit(fields, ...keys)); - } - }; -} -function Struct(fields, ...records) { - return makeTypeLiteralClass(fields, records); -} -/** -* Returns a property signature that represents a tag. -* A tag is a literal value that is used to distinguish between different types of objects. -* The tag is optional when using the `make` method. -* -* @example -* ```ts -* import * as assert from "node:assert" -* import { Schema } from "effect" -* -* const User = Schema.Struct({ -* _tag: Schema.tag("User"), -* name: Schema.String, -* age: Schema.Number -* }) -* -* assert.deepStrictEqual(User.make({ name: "John", age: 44 }), { _tag: "User", name: "John", age: 44 }) -* ``` -* -* @see {@link TaggedStruct} -* -* @since 3.10.0 -*/ -var tag = (tag) => Literal(tag).pipe(propertySignature, withConstructorDefault(() => tag)); -/** -* A tagged struct is a struct that has a tag property that is used to distinguish between different types of objects. -* -* The tag is optional when using the `make` method. -* -* @example -* ```ts -* import * as assert from "node:assert" -* import { Schema } from "effect" -* -* const User = Schema.TaggedStruct("User", { -* name: Schema.String, -* age: Schema.Number -* }) -* -* assert.deepStrictEqual(User.make({ name: "John", age: 44 }), { _tag: "User", name: "John", age: 44 }) -* ``` -* -* @category constructors -* @since 3.10.0 -*/ -var TaggedStruct = (value, fields) => Struct({ - _tag: tag(value), - ...fields -}); -/** -* @category constructors -* @since 3.10.0 -*/ -var suspend = (f) => make(new Suspend(() => f().ast)); -/** -* @since 3.10.0 -* @category symbol -*/ -var RefineSchemaId = /* @__PURE__ */ Symbol.for("effect/SchemaId/Refine"); -function makeRefineClass(from, filter, ast) { - return class RefineClass extends make(ast) { - static annotations(annotations) { - return makeRefineClass(this.from, this.filter, mergeSchemaAnnotations(this.ast, annotations)); + client.ids.add(id); + const params = typeof message.params === "object" && message.params !== null && !Array.isArray(message.params) ? message.params : {}; + if (!client.authenticated) { + const token = "token" in params && typeof params.token === "string" ? Buffer.from(params.token) : Buffer.alloc(0); + if (message.method !== "host.authenticate" || !tcpToken || token.length !== tcpToken.length || !(0, node_crypto.timingSafeEqual)(token, tcpToken)) { + reply(socket, error(message.id, "Authentication required")); + socket.end(); + return; } - static [RefineSchemaId] = from; - static from = from; - static filter = filter; - static make = (a, options) => { - return getDisableValidationMakeOption(options) ? a : validateSync(this)(a); - }; - }; -} -var fromFilterPredicateReturnTypeItem = (item, ast, input) => { - if (require_NodeRuntime.isBoolean(item)) return item ? require_NodeRuntime.none$1() : require_NodeRuntime.some(new Type(ast, input)); - if (require_NodeRuntime.isString(item)) return require_NodeRuntime.some(new Type(ast, input, item)); - if (item !== void 0) { - if ("_tag" in item) return require_NodeRuntime.some(item); - const issue = new Type(ast, input, item.message); - return require_NodeRuntime.some(require_NodeRuntime.isNonEmptyReadonlyArray(item.path) ? new Pointer(item.path, input, issue) : issue); - } - return require_NodeRuntime.none$1(); -}; -var toFilterParseIssue = (out, ast, input) => { - if (isSingle(out)) return fromFilterPredicateReturnTypeItem(out, ast, input); - if (require_NodeRuntime.isNonEmptyReadonlyArray(out)) { - const issues = require_NodeRuntime.filterMap(out, (issue) => fromFilterPredicateReturnTypeItem(issue, ast, input)); - if (require_NodeRuntime.isNonEmptyReadonlyArray(issues)) return require_NodeRuntime.some(issues.length === 1 ? issues[0] : new Composite(ast, input, issues)); + client.authenticated = true; + reply(socket, result(message.id, { authenticated: true })); + return; } - return require_NodeRuntime.none$1(); -}; -function filter(predicate, annotations) { - return (self) => { - function filter(input, options, ast) { - return toFilterParseIssue(predicate(input, options, ast), ast, input); - } - return makeRefineClass(self, filter, new Refinement$1(self.ast, filter, toASTAnnotations(annotations))); - }; -} -function makeTransformationClass(from, to, ast) { - return class TransformationClass extends make(ast) { - static annotations(annotations) { - return makeTransformationClass(this.from, this.to, mergeSchemaAnnotations(this.ast, annotations)); - } - static from = from; - static to = to; - }; -} -/** -* Create a new `Schema` by transforming the input and output of an existing `Schema` -* using the provided decoding functions. -* -* @category transformations -* @since 3.10.0 -*/ -var transformOrFail = /* @__PURE__ */ require_NodeRuntime.dual((args) => isSchema(args[0]) && isSchema(args[1]), (from, to, options) => makeTransformationClass(from, to, new Transformation$1(from.ast, to.ast, new FinalTransformation(options.decode, options.encode)))); -/** -* Create a new `Schema` by transforming the input and output of an existing `Schema` -* using the provided mapping functions. -* -* @category transformations -* @since 3.10.0 -*/ -var transform = /* @__PURE__ */ require_NodeRuntime.dual((args) => isSchema(args[0]) && isSchema(args[1]), (from, to, options) => transformOrFail(from, to, { - strict: true, - decode: (fromA, _options, _ast, toA) => succeed(options.decode(fromA, toA)), - encode: (toI, _options, _ast, toA) => succeed(options.encode(toI, toA)) -})); -/** -* @category schema id -* @since 3.10.0 -*/ -var TrimmedSchemaId = /* @__PURE__ */ Symbol.for("effect/SchemaId/Trimmed"); -/** -* Verifies that a string contains no leading or trailing whitespaces. -* -* Note. This combinator does not make any transformations, it only validates. -* If what you were looking for was a combinator to trim strings, then check out the `trim` combinator. -* -* @category string filters -* @since 3.10.0 -*/ -var trimmed = (annotations) => (self) => self.pipe(filter((a) => a === a.trim(), { - schemaId: TrimmedSchemaId, - title: "trimmed", - description: "a string with no leading or trailing whitespace", - jsonSchema: { pattern: "^\\S[\\s\\S]*\\S$|^\\S$|^$" }, - ...annotations -})); -/** -* @category schema id -* @since 3.10.0 -*/ -var MinLengthSchemaId = MinLengthSchemaId$1; -/** -* @category string filters -* @since 3.10.0 -*/ -var minLength = (minLength, annotations) => (self) => self.pipe(filter((a) => a.length >= minLength, { - schemaId: MinLengthSchemaId, - title: `minLength(${minLength})`, - description: `a string at least ${minLength} character(s) long`, - jsonSchema: { minLength }, - ...annotations -})); -/** -* @category schema id -* @since 3.10.0 -*/ -var LengthSchemaId = LengthSchemaId$1; -/** -* @category string filters -* @since 3.10.0 -*/ -var length = (length, annotations) => (self) => { - const minLength = require_NodeRuntime.isObject(length) ? Math.max(0, Math.floor(length.min)) : Math.max(0, Math.floor(length)); - const maxLength = require_NodeRuntime.isObject(length) ? Math.max(minLength, Math.floor(length.max)) : minLength; - if (minLength !== maxLength) return self.pipe(filter((a) => a.length >= minLength && a.length <= maxLength, { - schemaId: LengthSchemaId, - title: `length({ min: ${minLength}, max: ${maxLength})`, - description: `a string at least ${minLength} character(s) and at most ${maxLength} character(s) long`, - jsonSchema: { - minLength, - maxLength - }, - ...annotations - })); - return self.pipe(filter((a) => a.length === minLength, { - schemaId: LengthSchemaId, - title: `length(${minLength})`, - description: minLength === 1 ? `a single character` : `a string ${minLength} character(s) long`, - jsonSchema: { - minLength, - maxLength: minLength - }, - ...annotations - })); -}; -/** -* @category schema id -* @since 3.10.0 -*/ -var PatternSchemaId = /* @__PURE__ */ Symbol.for("effect/SchemaId/Pattern"); -/** -* @category string filters -* @since 3.10.0 -*/ -var pattern = (regex, annotations) => (self) => { - const source = regex.source; - return self.pipe(filter((a) => { - regex.lastIndex = 0; - return regex.test(a); - }, { - schemaId: PatternSchemaId, - [PatternSchemaId]: { regex }, - description: `a string matching the pattern ${source}`, - jsonSchema: { pattern: source }, - ...annotations - })); -}; -/** -* @category schema id -* @since 3.10.0 -*/ -var LowercasedSchemaId = /* @__PURE__ */ Symbol.for("effect/SchemaId/Lowercased"); -/** -* Verifies that a string is lowercased. -* -* @category string filters -* @since 3.10.0 -*/ -var lowercased = (annotations) => (self) => self.pipe(filter((a) => a === a.toLowerCase(), { - schemaId: LowercasedSchemaId, - title: "lowercased", - description: "a lowercase string", - jsonSchema: { pattern: "^[^A-Z]*$" }, - ...annotations -})); -/** -* @category string constructors -* @since 3.10.0 -*/ -var Lowercased = class extends String$.pipe(/* @__PURE__ */ lowercased({ identifier: "Lowercased" })) {}; -/** -* @category schema id -* @since 3.10.0 -*/ -var UppercasedSchemaId = /* @__PURE__ */ Symbol.for("effect/SchemaId/Uppercased"); -/** -* Verifies that a string is uppercased. -* -* @category string filters -* @since 3.10.0 -*/ -var uppercased = (annotations) => (self) => self.pipe(filter((a) => a === a.toUpperCase(), { - schemaId: UppercasedSchemaId, - title: "uppercased", - description: "an uppercase string", - jsonSchema: { pattern: "^[^a-z]*$" }, - ...annotations -})); -/** -* @category string constructors -* @since 3.10.0 -*/ -var Uppercased = class extends String$.pipe(/* @__PURE__ */ uppercased({ identifier: "Uppercased" })) {}; -/** -* @category schema id -* @since 3.10.0 -*/ -var CapitalizedSchemaId = /* @__PURE__ */ Symbol.for("effect/SchemaId/Capitalized"); -/** -* Verifies that a string is capitalized. -* -* @category string filters -* @since 3.10.0 -*/ -var capitalized = (annotations) => (self) => self.pipe(filter((a) => a[0]?.toUpperCase() === a[0], { - schemaId: CapitalizedSchemaId, - title: "capitalized", - description: "a capitalized string", - jsonSchema: { pattern: "^[^a-z]?.*$" }, - ...annotations -})); -/** -* @category string constructors -* @since 3.10.0 -*/ -var Capitalized = class extends String$.pipe(/* @__PURE__ */ capitalized({ identifier: "Capitalized" })) {}; -/** -* @category schema id -* @since 3.10.0 -*/ -var UncapitalizedSchemaId = /* @__PURE__ */ Symbol.for("effect/SchemaId/Uncapitalized"); -/** -* Verifies that a string is uncapitalized. -* -* @category string filters -* @since 3.10.0 -*/ -var uncapitalized = (annotations) => (self) => self.pipe(filter((a) => a[0]?.toLowerCase() === a[0], { - schemaId: UncapitalizedSchemaId, - title: "uncapitalized", - description: "a uncapitalized string", - jsonSchema: { pattern: "^[^A-Z]?.*$" }, - ...annotations -})); -/** -* @category string constructors -* @since 3.10.0 -*/ -var Uncapitalized = class extends String$.pipe(/* @__PURE__ */ uncapitalized({ identifier: "Uncapitalized" })) {}; -String$.pipe(/* @__PURE__ */ length(1, { identifier: "Char" })); -/** -* @category string filters -* @since 3.10.0 -*/ -var nonEmptyString = (annotations) => minLength(1, { - title: "nonEmptyString", - description: "a non empty string", - ...annotations -}); -transform(String$.annotations({ description: "a string that will be converted to lowercase" }), Lowercased, { - strict: true, - decode: (i) => i.toLowerCase(), - encode: require_NodeRuntime.identity -}).annotations({ identifier: "Lowercase" }); -transform(String$.annotations({ description: "a string that will be converted to uppercase" }), Uppercased, { - strict: true, - decode: (i) => i.toUpperCase(), - encode: require_NodeRuntime.identity -}).annotations({ identifier: "Uppercase" }); -transform(String$.annotations({ description: "a string that will be converted to a capitalized format" }), Capitalized, { - strict: true, - decode: (i) => capitalize(i), - encode: require_NodeRuntime.identity -}).annotations({ identifier: "Capitalize" }); -transform(String$.annotations({ description: "a string that will be converted to an uncapitalized format" }), Uncapitalized, { - strict: true, - decode: (i) => uncapitalize(i), - encode: require_NodeRuntime.identity -}).annotations({ identifier: "Uncapitalize" }); -/** -* @category string constructors -* @since 3.10.0 -*/ -var Trimmed = class extends String$.pipe(/* @__PURE__ */ trimmed({ identifier: "Trimmed" })) {}; -/** -* Useful for validating strings that must contain meaningful characters without -* leading or trailing whitespace. -* -* @example -* ```ts -* import { Schema } from "effect" -* -* console.log(Schema.decodeOption(Schema.NonEmptyTrimmedString)("")) // Option.none() -* console.log(Schema.decodeOption(Schema.NonEmptyTrimmedString)(" a ")) // Option.none() -* console.log(Schema.decodeOption(Schema.NonEmptyTrimmedString)("a")) // Option.some("a") -* ``` -* -* @category string constructors -* @since 3.10.0 -*/ -var NonEmptyTrimmedString = class extends Trimmed.pipe(/* @__PURE__ */ nonEmptyString({ identifier: "NonEmptyTrimmedString" })) {}; -transform(String$.annotations({ description: "a string that will be trimmed" }), Trimmed, { - strict: true, - decode: (i) => i.trim(), - encode: require_NodeRuntime.identity -}).annotations({ identifier: "Trim" }); -var getErrorMessage = (e) => e instanceof Error ? e.message : String(e); -String$.pipe(/* @__PURE__ */ nonEmptyString({ identifier: "NonEmptyString" })); -/** -* @category schema id -* @since 3.10.0 -*/ -var UUIDSchemaId = /* @__PURE__ */ Symbol.for("effect/SchemaId/UUID"); -var uuidRegexp = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/i; -String$.pipe(/* @__PURE__ */ pattern(uuidRegexp, { - schemaId: UUIDSchemaId, - identifier: "UUID", - jsonSchema: { - format: "uuid", - pattern: uuidRegexp.source - }, - description: "a Universally Unique Identifier", - arbitrary: () => (fc) => fc.uuid() -})); -/** -* @category schema id -* @since 3.10.0 -*/ -var ULIDSchemaId = /* @__PURE__ */ Symbol.for("effect/SchemaId/ULID"); -String$.pipe(/* @__PURE__ */ pattern(/^[0-7][0-9A-HJKMNP-TV-Z]{25}$/i, { - schemaId: ULIDSchemaId, - identifier: "ULID", - description: "a Universally Unique Lexicographically Sortable Identifier", - arbitrary: () => (fc) => fc.ulid() -})); -/** -* Defines a schema that represents a `URL` object. -* -* @category URL constructors -* @since 3.11.0 -*/ -var URLFromSelf = class extends instanceOf(URL, { - typeConstructor: { _tag: "URL" }, - identifier: "URLFromSelf", - arbitrary: () => (fc) => fc.webUrl().map((s) => new URL(s)), - pretty: () => (url) => url.toString() -}) {}; -transformOrFail(String$.annotations({ description: "a string to be decoded into a URL" }), URLFromSelf, { - strict: true, - decode: (i, _, ast) => _try({ - try: () => new URL(i), - catch: (e) => new Type(ast, i, `Unable to decode ${JSON.stringify(i)} into a URL. ${getErrorMessage(e)}`) - }), - encode: (a) => succeed(a.toString()) -}).annotations({ - identifier: "URL", - pretty: () => (url) => url.toString() -}); -/** -* @category schema id -* @since 3.10.0 -*/ -var FiniteSchemaId = FiniteSchemaId$1; -/** -* Ensures that the provided value is a finite number (excluding NaN, +Infinity, and -Infinity). -* -* @category number filters -* @since 3.10.0 -*/ -var finite = (annotations) => (self) => self.pipe(filter(Number.isFinite, { - schemaId: FiniteSchemaId, - title: "finite", - description: "a finite number", - jsonSchema: {}, - ...annotations -})); -/** -* @category schema id -* @since 3.10.0 -*/ -var GreaterThanSchemaId = GreaterThanSchemaId$1; -/** -* This filter checks whether the provided number is greater than the specified minimum. -* -* @category number filters -* @since 3.10.0 -*/ -var greaterThan = (exclusiveMinimum, annotations) => (self) => self.pipe(filter((a) => a > exclusiveMinimum, { - schemaId: GreaterThanSchemaId, - title: `greaterThan(${exclusiveMinimum})`, - description: exclusiveMinimum === 0 ? "a positive number" : `a number greater than ${exclusiveMinimum}`, - jsonSchema: { exclusiveMinimum }, - ...annotations -})); -/** -* @category schema id -* @since 3.10.0 -*/ -var GreaterThanOrEqualToSchemaId = GreaterThanOrEqualToSchemaId$1; -/** -* This filter checks whether the provided number is greater than or equal to the specified minimum. -* -* @category number filters -* @since 3.10.0 -*/ -var greaterThanOrEqualTo = (minimum, annotations) => (self) => self.pipe(filter((a) => a >= minimum, { - schemaId: GreaterThanOrEqualToSchemaId, - title: `greaterThanOrEqualTo(${minimum})`, - description: minimum === 0 ? "a non-negative number" : `a number greater than or equal to ${minimum}`, - jsonSchema: { minimum }, - ...annotations -})); -/** -* @category schema id -* @since 3.10.0 -*/ -var IntSchemaId = IntSchemaId$1; -/** -* Ensures that the provided value is an integer number (excluding NaN, +Infinity, and -Infinity). -* -* @category number filters -* @since 3.10.0 -*/ -var int = (annotations) => (self) => self.pipe(filter((a) => Number.isSafeInteger(a), { - schemaId: IntSchemaId, - title: "int", - description: "an integer", - jsonSchema: { type: "integer" }, - ...annotations -})); -/** -* @category schema id -* @since 3.10.0 -*/ -var LessThanSchemaId = LessThanSchemaId$1; -/** -* This filter checks whether the provided number is less than the specified maximum. -* -* @category number filters -* @since 3.10.0 -*/ -var lessThan = (exclusiveMaximum, annotations) => (self) => self.pipe(filter((a) => a < exclusiveMaximum, { - schemaId: LessThanSchemaId, - title: `lessThan(${exclusiveMaximum})`, - description: exclusiveMaximum === 0 ? "a negative number" : `a number less than ${exclusiveMaximum}`, - jsonSchema: { exclusiveMaximum }, - ...annotations -})); -/** -* @category schema id -* @since 3.10.0 -*/ -var LessThanOrEqualToSchemaId = LessThanOrEqualToSchemaId$1; -/** -* This schema checks whether the provided number is less than or equal to the specified maximum. -* -* @category number filters -* @since 3.10.0 -*/ -var lessThanOrEqualTo = (maximum, annotations) => (self) => self.pipe(filter((a) => a <= maximum, { - schemaId: LessThanOrEqualToSchemaId, - title: `lessThanOrEqualTo(${maximum})`, - description: maximum === 0 ? "a non-positive number" : `a number less than or equal to ${maximum}`, - jsonSchema: { maximum }, - ...annotations -})); -/** -* @category schema id -* @since 3.10.0 -*/ -var BetweenSchemaId = BetweenSchemaId$1; -/** -* This filter checks whether the provided number falls within the specified minimum and maximum values. -* -* @category number filters -* @since 3.10.0 -*/ -var between = (minimum, maximum, annotations) => (self) => self.pipe(filter((a) => a >= minimum && a <= maximum, { - schemaId: BetweenSchemaId, - title: `between(${minimum}, ${maximum})`, - description: `a number between ${minimum} and ${maximum}`, - jsonSchema: { - minimum, - maximum - }, - ...annotations -})); -/** -* @category schema id -* @since 3.10.0 -*/ -var NonNaNSchemaId = NonNaNSchemaId$1; -/** -* @category number filters -* @since 3.10.0 -*/ -var nonNaN = (annotations) => (self) => self.pipe(filter((a) => !Number.isNaN(a), { - schemaId: NonNaNSchemaId, - title: "nonNaN", - description: "a number excluding NaN", - ...annotations -})); -/** -* @category number filters -* @since 3.10.0 -*/ -var positive = (annotations) => greaterThan(0, { - title: "positive", - ...annotations -}); -/** -* @category number filters -* @since 3.10.0 -*/ -var negative = (annotations) => lessThan(0, { - title: "negative", - ...annotations -}); -/** -* @category number filters -* @since 3.10.0 -*/ -var nonPositive = (annotations) => lessThanOrEqualTo(0, { - title: "nonPositive", - ...annotations -}); -/** -* @category number filters -* @since 3.10.0 -*/ -var nonNegative = (annotations) => greaterThanOrEqualTo(0, { - title: "nonNegative", - ...annotations -}); -/** -* Transforms a `string` into a `number` by parsing the string using the `parse` -* function of the `effect/Number` module. -* -* It returns an error if the value can't be converted (for example when -* non-numeric characters are provided). -* -* The following special string values are supported: "NaN", "Infinity", -* "-Infinity". -* -* @category number transformations -* @since 3.10.0 -*/ -function parseNumber(self) { - return transformOrFail(self, Number$, { - strict: false, - decode: (i, _, ast) => fromOption(require_NodeRuntime.parse(i), () => new Type(ast, i, `Unable to decode ${JSON.stringify(i)} into a number`)), - encode: (a) => succeed(String(a)) - }); -} -parseNumber(String$.annotations({ description: "a string to be decoded into a number" })).annotations({ identifier: "NumberFromString" }); -Number$.pipe(/* @__PURE__ */ finite({ identifier: "Finite" })); -/** -* @category number constructors -* @since 3.10.0 -*/ -var Int = class extends Number$.pipe(/* @__PURE__ */ int({ identifier: "Int" })) {}; -Number$.pipe(/* @__PURE__ */ nonNaN({ identifier: "NonNaN" })); -Number$.pipe(/* @__PURE__ */ positive({ identifier: "Positive" })); -Number$.pipe(/* @__PURE__ */ negative({ identifier: "Negative" })); -Number$.pipe(/* @__PURE__ */ nonPositive({ identifier: "NonPositive" })); -/** -* @category number constructors -* @since 3.10.0 -*/ -var NonNegative = class extends Number$.pipe(/* @__PURE__ */ nonNegative({ identifier: "NonNegative" })) {}; -/** -* @category schema id -* @since 3.10.0 -*/ -var JsonNumberSchemaId = JsonNumberSchemaId$1; -Number$.pipe(/* @__PURE__ */ finite({ - schemaId: JsonNumberSchemaId, - identifier: "JsonNumber" -})); -transform(/* @__PURE__ */ Boolean$.annotations({ description: "a boolean that will be negated" }), Boolean$, { - strict: true, - decode: (i) => require_NodeRuntime.not(i), - encode: (a) => require_NodeRuntime.not(a) -}); -var encodeSymbol = (sym, ast) => { - const key = Symbol.keyFor(sym); - return key === void 0 ? fail(new Type(ast, sym, `Unable to encode a unique symbol ${String(sym)} into a string`)) : succeed(key); -}; -var decodeSymbol = (s) => succeed(Symbol.for(s)); -transformOrFail(String$.annotations({ description: "a string to be decoded into a globally shared symbol" }), SymbolFromSelf, { - strict: false, - decode: (i) => decodeSymbol(i), - encode: (a, _, ast) => encodeSymbol(a, ast) -}).annotations({ identifier: "Symbol" }); -/** -* @category schema id -* @since 3.10.0 -*/ -var GreaterThanOrEqualToBigIntSchemaId = GreaterThanOrEqualToBigIntSchemaId$1; -/** -* @category bigint filters -* @since 3.10.0 -*/ -var greaterThanOrEqualToBigInt = (min, annotations) => (self) => self.pipe(filter((a) => a >= min, { - schemaId: GreaterThanOrEqualToBigIntSchemaId, - [GreaterThanOrEqualToBigIntSchemaId]: { min }, - title: `greaterThanOrEqualToBigInt(${min})`, - description: min === 0n ? "a non-negative bigint" : `a bigint greater than or equal to ${min}n`, - ...annotations -})); -/** -* @category schema id -* @since 3.10.0 -*/ -var BetweenBigIntSchemaId = BetweenBigintSchemaId; -/** -* @category bigint filters -* @since 3.10.0 -*/ -var betweenBigInt = (min, max, annotations) => (self) => self.pipe(filter((a) => a >= min && a <= max, { - schemaId: BetweenBigIntSchemaId, - [BetweenBigIntSchemaId]: { - min, - max - }, - title: `betweenBigInt(${min}, ${max})`, - description: `a bigint between ${min}n and ${max}n`, - ...annotations -})); -/** -* @category bigint filters -* @since 3.10.0 -*/ -var nonNegativeBigInt = (annotations) => greaterThanOrEqualToBigInt(0n, { - title: "nonNegativeBigInt", - ...annotations -}); -/** @ignore */ -var BigInt$ = class extends transformOrFail(String$.annotations({ description: "a string to be decoded into a bigint" }), BigIntFromSelf, { - strict: true, - decode: (i, _, ast) => fromOption(fromString(i), () => new Type(ast, i, `Unable to decode ${JSON.stringify(i)} into a bigint`)), - encode: (a) => succeed(String(a)) -}).annotations({ identifier: "BigInt" }) {}; -/** -* @category bigint constructors -* @since 3.10.0 -*/ -var NonNegativeBigIntFromSelf = /* @__PURE__ */ BigIntFromSelf.pipe(/* @__PURE__ */ nonNegativeBigInt({ identifier: "NonNegativeBigintFromSelf" })); -transformOrFail(Number$.annotations({ description: "a number to be decoded into a bigint" }), BigIntFromSelf.pipe(betweenBigInt(BigInt(Number.MIN_SAFE_INTEGER), BigInt(Number.MAX_SAFE_INTEGER))), { - strict: true, - decode: (i, _, ast) => fromOption(fromNumber(i), () => new Type(ast, i, `Unable to decode ${i} into a bigint`)), - encode: (a, _, ast) => fromOption(toNumber(a), () => new Type(ast, a, `Unable to encode ${a}n into a number`)) -}).annotations({ identifier: "BigIntFromNumber" }); -var toComposite = (eff, onSuccess, ast, actual) => mapBoth(eff, { - onFailure: (e) => new Composite(ast, actual, e), - onSuccess -}); -/** -* @category Duration constructors -* @since 3.10.0 -*/ -var DurationFromSelf = class extends declare(require_NodeRuntime.isDuration, { - typeConstructor: { _tag: "effect/Duration" }, - identifier: "DurationFromSelf", - pretty: () => String, - arbitrary: () => (fc) => fc.oneof(fc.constant(require_NodeRuntime.infinity), fc.bigInt({ min: 0n }).map((_) => require_NodeRuntime.nanos(_)), fc.maxSafeNat().map((_) => require_NodeRuntime.millis(_))), - equivalence: () => require_NodeRuntime.Equivalence -}) {}; -transformOrFail(NonNegativeBigIntFromSelf.annotations({ description: "a bigint to be decoded into a Duration" }), DurationFromSelf.pipe(filter((duration) => require_NodeRuntime.isFinite(duration), { description: "a finite duration" })), { - strict: true, - decode: (i) => succeed(require_NodeRuntime.nanos(i)), - encode: (a, _, ast) => require_NodeRuntime.match(require_NodeRuntime.toNanos(a), { - onNone: () => fail(new Type(ast, a, `Unable to encode ${a} into a bigint`)), - onSome: (nanos) => succeed(nanos) - }) -}).annotations({ identifier: "DurationFromNanos" }); -/** -* A non-negative integer. +Infinity is excluded. -* -* @category number constructors -* @since 3.11.10 -*/ -var NonNegativeInt = /* @__PURE__ */ NonNegative.pipe(int()).annotations({ identifier: "NonNegativeInt" }); -transform(NonNegative.annotations({ description: "a non-negative number to be decoded into a Duration" }), DurationFromSelf, { - strict: true, - decode: (i) => require_NodeRuntime.millis(i), - encode: (a) => require_NodeRuntime.toMillis(a) -}).annotations({ identifier: "DurationFromMillis" }); -var DurationValueMillis = /* @__PURE__ */ TaggedStruct("Millis", { millis: NonNegativeInt }); -var DurationValueNanos = /* @__PURE__ */ TaggedStruct("Nanos", { nanos: BigInt$ }); -var DurationValueInfinity = /* @__PURE__ */ TaggedStruct("Infinity", {}); -var durationValueInfinity = /* @__PURE__ */ DurationValueInfinity.make({}); -var DurationValue = /* @__PURE__ */ Union(DurationValueMillis, DurationValueNanos, DurationValueInfinity).annotations({ - identifier: "DurationValue", - description: "an JSON-compatible tagged union to be decoded into a Duration" -}); -var HRTime = /* @__PURE__ */ Union(/* @__PURE__ */ Tuple(element(NonNegativeInt).annotations({ title: "seconds" }), element(NonNegativeInt).annotations({ title: "nanos" })).annotations({ identifier: "FiniteHRTime" }), /* @__PURE__ */ Tuple(Literal(-1), Literal(0)).annotations({ identifier: "InfiniteHRTime" })).annotations({ - identifier: "HRTime", - description: "a tuple of seconds and nanos to be decoded into a Duration" -}); -var isDurationValue = (u) => typeof u === "object"; -transform(Union(DurationValue, HRTime), DurationFromSelf, { - strict: true, - decode: (i) => { - if (isDurationValue(i)) switch (i._tag) { - case "Millis": return require_NodeRuntime.millis(i.millis); - case "Nanos": return require_NodeRuntime.nanos(i.nanos); - case "Infinity": return require_NodeRuntime.infinity; - } - const [seconds, nanos$1] = i; - return seconds === -1 ? require_NodeRuntime.infinity : require_NodeRuntime.nanos(BigInt(seconds) * BigInt(1e9) + BigInt(nanos$1)); - }, - encode: (a) => { - switch (a.value._tag) { - case "Millis": return DurationValueMillis.make({ millis: a.value.millis }); - case "Nanos": return DurationValueNanos.make({ nanos: a.value.nanos }); - case "Infinity": return durationValueInfinity; - } + if (message.method === "host.ping") { + reply(socket, result(message.id, "pong")); + return; } -}).annotations({ identifier: "Duration" }); -/** -* @category Uint8Array constructors -* @since 3.10.0 -*/ -var Uint8ArrayFromSelf = class extends declare(require_NodeRuntime.isUint8Array, { - typeConstructor: { _tag: "Uint8Array" }, - identifier: "Uint8ArrayFromSelf", - pretty: () => (u8arr) => `new Uint8Array(${JSON.stringify(Array.from(u8arr))})`, - arbitrary: () => (fc) => fc.uint8Array(), - equivalence: () => require_NodeRuntime.getEquivalence(require_NodeRuntime.equals) -}) {}; -/** -* @category number constructors -* @since 3.11.10 -*/ -var Uint8 = class extends Number$.pipe(/* @__PURE__ */ between(0, 255, { - identifier: "Uint8", - description: "a 8-bit unsigned integer" -})) {}; -transform(Array$(Uint8).annotations({ description: "an array of 8-bit unsigned integers to be decoded into a Uint8Array" }), Uint8ArrayFromSelf, { - strict: true, - decode: (i) => Uint8Array.from(i), - encode: (a) => Array.from(a) -}).annotations({ identifier: "Uint8Array" }); -/** -* @category schema id -* @since 3.10.0 -*/ -var ValidDateSchemaId = /* @__PURE__ */ Symbol.for("effect/SchemaId/ValidDate"); -/** -* Defines a filter that specifically rejects invalid dates, such as `new -* Date("Invalid Date")`. This filter ensures that only properly formatted and -* valid date objects are accepted, enhancing data integrity by preventing -* erroneous date values from being processed. -* -* @category Date filters -* @since 3.10.0 -*/ -var validDate = (annotations) => (self) => self.pipe(filter((a) => !Number.isNaN(a.getTime()), { - schemaId: ValidDateSchemaId, - [ValidDateSchemaId]: { noInvalidDate: true }, - title: "validDate", - description: "a valid Date", - ...annotations -})); -/** -* @category schema id -* @since 3.11.8 -*/ -var DateFromSelfSchemaId = DateFromSelfSchemaId$1; -/** -* Describes a schema that accommodates potentially invalid `Date` instances, -* such as `new Date("Invalid Date")`, without rejection. -* -* @category Date constructors -* @since 3.10.0 -*/ -var DateFromSelf = class extends declare(require_NodeRuntime.isDate, { - typeConstructor: { _tag: "Date" }, - identifier: "DateFromSelf", - schemaId: DateFromSelfSchemaId, - [DateFromSelfSchemaId]: { noInvalidDate: false }, - description: "a potentially invalid Date instance", - pretty: () => (date) => `new Date(${JSON.stringify(date)})`, - arbitrary: () => (fc) => fc.date({ noInvalidDate: false }), - equivalence: () => require_NodeRuntime.Date -}) {}; -DateFromSelf.pipe(/* @__PURE__ */ validDate({ - identifier: "ValidDateFromSelf", - description: "a valid Date instance" -})); -/** -* Defines a schema that attempts to convert a `string` to a `Date` object using -* the `new Date` constructor. This conversion is lenient, meaning it does not -* reject strings that do not form valid dates (e.g., using `new Date("Invalid -* Date")` results in a `Date` object, despite being invalid). -* -* @category Date transformations -* @since 3.10.0 -*/ -var DateFromString = class extends transform(String$.annotations({ description: "a string to be decoded into a Date" }), DateFromSelf, { - strict: true, - decode: (i) => new Date(i), - encode: (a) => require_NodeRuntime.formatDate(a) -}).annotations({ identifier: "DateFromString" }) {}; -DateFromString.pipe(/* @__PURE__ */ validDate({ identifier: "Date" })); -transform(Number$.annotations({ description: "a number to be decoded into a Date" }), DateFromSelf, { - strict: true, - decode: (i) => new Date(i), - encode: (a) => a.getTime() -}).annotations({ identifier: "DateFromNumber" }); -/** -* Describes a schema that represents a `DateTime.Utc` instance. -* -* @category DateTime.Utc constructors -* @since 3.10.0 -*/ -var DateTimeUtcFromSelf = class extends declare((u) => isDateTime(u) && isUtc(u), { - typeConstructor: { _tag: "effect/DateTime.Utc" }, - identifier: "DateTimeUtcFromSelf", - description: "a DateTime.Utc instance", - pretty: () => (dateTime) => dateTime.toString(), - arbitrary: () => (fc) => fc.date({ noInvalidDate: true }).map((date) => unsafeFromDate(date)), - equivalence: () => Equivalence -}) {}; -var decodeDateTimeUtc = (input, ast) => _try({ - try: () => unsafeMake(input), - catch: () => new Type(ast, input, `Unable to decode ${require_NodeRuntime.formatUnknown(input)} into a DateTime.Utc`) -}); -transformOrFail(Number$.annotations({ description: "a number to be decoded into a DateTime.Utc" }), DateTimeUtcFromSelf, { - strict: true, - decode: (i, _, ast) => decodeDateTimeUtc(i, ast), - encode: (a) => succeed(toEpochMillis(a)) -}).annotations({ identifier: "DateTimeUtcFromNumber" }); -transformOrFail(DateFromSelf.annotations({ description: "a Date to be decoded into a DateTime.Utc" }), DateTimeUtcFromSelf, { - strict: true, - decode: (i, _, ast) => decodeDateTimeUtc(i, ast), - encode: (a) => succeed(toDateUtc(a)) -}).annotations({ identifier: "DateTimeUtcFromDate" }); -transformOrFail(String$.annotations({ description: "a string to be decoded into a DateTime.Utc" }), DateTimeUtcFromSelf, { - strict: true, - decode: (i, _, ast) => decodeDateTimeUtc(i, ast), - encode: (a) => succeed(formatIso(a)) -}).annotations({ identifier: "DateTimeUtc" }); -var timeZoneOffsetArbitrary = () => (fc) => fc.integer({ - min: -720 * 60 * 1e3, - max: 840 * 60 * 1e3 -}).map(zoneMakeOffset); -/** -* Describes a schema that represents a `TimeZone.Offset` instance. -* -* @category TimeZone constructors -* @since 3.10.0 -*/ -var TimeZoneOffsetFromSelf = class extends declare(isTimeZoneOffset, { - typeConstructor: { _tag: "effect/DateTime.TimeZone.Offset" }, - identifier: "TimeZoneOffsetFromSelf", - description: "a TimeZone.Offset instance", - pretty: () => (zone) => zone.toString(), - arbitrary: timeZoneOffsetArbitrary -}) {}; -transform(Number$.annotations({ description: "a number to be decoded into a TimeZone.Offset" }), TimeZoneOffsetFromSelf, { - strict: true, - decode: (i) => zoneMakeOffset(i), - encode: (a) => a.offset -}).annotations({ identifier: "TimeZoneOffset" }); -var timeZoneNamedArbitrary = () => (fc) => fc.constantFrom(...Intl.supportedValuesOf("timeZone")).map(zoneUnsafeMakeNamed); -/** -* Describes a schema that represents a `TimeZone.Named` instance. -* -* @category TimeZone constructors -* @since 3.10.0 -*/ -var TimeZoneNamedFromSelf = class extends declare(isTimeZoneNamed, { - typeConstructor: { _tag: "effect/DateTime.TimeZone.Named" }, - identifier: "TimeZoneNamedFromSelf", - description: "a TimeZone.Named instance", - pretty: () => (zone) => zone.toString(), - arbitrary: timeZoneNamedArbitrary -}) {}; -transformOrFail(String$.annotations({ description: "a string to be decoded into a TimeZone.Named" }), TimeZoneNamedFromSelf, { - strict: true, - decode: (i, _, ast) => _try({ - try: () => zoneUnsafeMakeNamed(i), - catch: () => new Type(ast, i, `Unable to decode ${JSON.stringify(i)} into a TimeZone.Named`) - }), - encode: (a) => succeed(a.id) -}).annotations({ identifier: "TimeZoneNamed" }); -/** -* @category TimeZone constructors -* @since 3.10.0 -*/ -var TimeZoneFromSelf = class extends Union(TimeZoneOffsetFromSelf, TimeZoneNamedFromSelf) {}; -transformOrFail(String$.annotations({ description: "a string to be decoded into a TimeZone" }), TimeZoneFromSelf, { - strict: true, - decode: (i, _, ast) => require_NodeRuntime.match(zoneFromString(i), { - onNone: () => fail(new Type(ast, i, `Unable to decode ${JSON.stringify(i)} into a TimeZone`)), - onSome: succeed - }), - encode: (a) => succeed(zoneToString(a)) -}).annotations({ identifier: "TimeZone" }); -var timeZoneArbitrary = (fc) => fc.oneof(timeZoneOffsetArbitrary()(fc), timeZoneNamedArbitrary()(fc)); -/** -* Describes a schema that represents a `DateTime.Zoned` instance. -* -* @category DateTime.Zoned constructors -* @since 3.10.0 -*/ -var DateTimeZonedFromSelf = class extends declare((u) => isDateTime(u) && isZoned(u), { - typeConstructor: { _tag: "effect/DateTime.Zoned" }, - identifier: "DateTimeZonedFromSelf", - description: "a DateTime.Zoned instance", - pretty: () => (dateTime) => dateTime.toString(), - arbitrary: () => (fc) => fc.tuple(fc.integer({ - min: -31536e9, - max: 31536e9 - }), timeZoneArbitrary(fc)).map(([millis, timeZone]) => unsafeMakeZoned(millis, { timeZone })), - equivalence: () => Equivalence -}) {}; -transformOrFail(String$.annotations({ description: "a string to be decoded into a DateTime.Zoned" }), DateTimeZonedFromSelf, { - strict: true, - decode: (i, _, ast) => require_NodeRuntime.match(makeZonedFromString(i), { - onNone: () => fail(new Type(ast, i, `Unable to decode ${JSON.stringify(i)} into a DateTime.Zoned`)), - onSome: succeed - }), - encode: (a) => succeed(formatIsoZoned(a)) -}).annotations({ identifier: "DateTimeZoned" }); -var optionDecode = (input) => input._tag === "None" ? require_NodeRuntime.none$1() : require_NodeRuntime.some(input.value); -var optionArbitrary = (value, ctx) => (fc) => fc.oneof(ctx, fc.record({ _tag: fc.constant("None") }), fc.record({ - _tag: fc.constant("Some"), - value: value(fc) -})).map(optionDecode); -var optionPretty = (value) => require_NodeRuntime.match({ - onNone: () => "none()", - onSome: (a) => `some(${value(a)})` -}); -var optionParse = (decodeUnknown) => (u, options, ast) => require_NodeRuntime.isOption(u) ? require_NodeRuntime.isNone(u) ? succeed(require_NodeRuntime.none$1()) : toComposite(decodeUnknown(u.value, options), require_NodeRuntime.some, ast, u) : fail(new Type(ast, u)); -var OptionFromSelf_ = (value) => { - return declare([value], { - decode: (value) => optionParse(decodeUnknown(value)), - encode: (value) => optionParse(encodeUnknown(value)) - }, { - typeConstructor: { _tag: "effect/Option" }, - pretty: optionPretty, - arbitrary: optionArbitrary, - equivalence: require_NodeRuntime.getEquivalence$1 - }); -}; -/** -* @category Option transformations -* @since 3.10.0 -*/ -var OptionFromSelf = (value) => { - return OptionFromSelf_(value).annotations({ description: `Option<${format(value)}>` }); -}; -transform(String$, /* @__PURE__ */ OptionFromSelf(NonEmptyTrimmedString), { - strict: true, - decode: (i) => require_NodeRuntime.filter(require_NodeRuntime.some(i.trim()), isNonEmpty), - encode: (a) => require_NodeRuntime.getOrElse(a, () => "") -}); -var bigDecimalPretty = () => (val) => `BigDecimal(${format$1(normalize(val))})`; -var bigDecimalArbitrary = () => (fc) => fc.tuple(fc.bigInt(), fc.integer({ - min: -18, - max: 18 -})).map(([value, scale]) => make$1(value, scale)); -/** -* @category BigDecimal constructors -* @since 3.10.0 -*/ -var BigDecimalFromSelf = class extends declare(isBigDecimal, { - typeConstructor: { _tag: "effect/BigDecimal" }, - identifier: "BigDecimalFromSelf", - pretty: bigDecimalPretty, - arbitrary: bigDecimalArbitrary, - equivalence: () => Equivalence$2 -}) {}; -transformOrFail(String$.annotations({ description: "a string to be decoded into a BigDecimal" }), BigDecimalFromSelf, { - strict: true, - decode: (i, _, ast) => fromString$1(i).pipe(require_NodeRuntime.match({ - onNone: () => fail(new Type(ast, i, `Unable to decode ${JSON.stringify(i)} into a BigDecimal`)), - onSome: (val) => succeed(normalize(val)) - })), - encode: (a) => succeed(format$1(normalize(a))) -}).annotations({ identifier: "BigDecimal" }); -transform(Number$.annotations({ description: "a number to be decoded into a BigDecimal" }), BigDecimalFromSelf, { - strict: true, - decode: (i) => unsafeFromNumber(i), - encode: (a) => unsafeToNumber(a) -}).annotations({ identifier: "BigDecimalFromNumber" }); -function getDisableValidationMakeOption(options) { - return require_NodeRuntime.isBoolean(options) ? options : options?.disableValidation ?? false; -} -var FiberIdEncoded = /* @__PURE__ */ Union(/* @__PURE__ */ Struct({ _tag: Literal("None") }).annotations({ identifier: "FiberIdNoneEncoded" }), /* @__PURE__ */ Struct({ - _tag: Literal("Runtime"), - id: Int, - startTimeMillis: Int -}).annotations({ identifier: "FiberIdRuntimeEncoded" }), /* @__PURE__ */ Struct({ - _tag: Literal("Composite"), - left: suspend(() => FiberIdEncoded), - right: suspend(() => FiberIdEncoded) -}).annotations({ identifier: "FiberIdCompositeEncoded" })).annotations({ identifier: "FiberIdEncoded" }); -var fiberIdArbitrary = (fc) => fc.letrec((tie) => ({ - None: fc.record({ _tag: fc.constant("None") }), - Runtime: fc.record({ - _tag: fc.constant("Runtime"), - id: fc.integer(), - startTimeMillis: fc.integer() - }), - Composite: fc.record({ - _tag: fc.constant("Composite"), - left: tie("FiberId"), - right: tie("FiberId") - }), - FiberId: fc.oneof(tie("None"), tie("Runtime"), tie("Composite")) -})).FiberId.map(fiberIdDecode); -var fiberIdPretty = (fiberId) => { - switch (fiberId._tag) { - case "None": return "FiberId.none"; - case "Runtime": return `FiberId.runtime(${fiberId.id}, ${fiberId.startTimeMillis})`; - case "Composite": return `FiberId.composite(${fiberIdPretty(fiberId.right)}, ${fiberIdPretty(fiberId.left)})`; + if (message.method === "host.info") { + reply(socket, result(message.id, { + protocolVersion: 2, + extensionProtocol, + epoch, + session_id: client.session, + reconnect: "new-session-no-replay" + })); + return; } -}; -/** -* @category FiberId constructors -* @since 3.10.0 -*/ -var FiberIdFromSelf = class extends declare(require_NodeRuntime.isFiberId, { - typeConstructor: { _tag: "effect/FiberId" }, - identifier: "FiberIdFromSelf", - pretty: () => fiberIdPretty, - arbitrary: () => fiberIdArbitrary -}) {}; -var fiberIdDecode = (input) => { - switch (input._tag) { - case "None": return require_NodeRuntime.none; - case "Runtime": return require_NodeRuntime.runtime(input.id, input.startTimeMillis); - case "Composite": return require_NodeRuntime.composite(fiberIdDecode(input.left), fiberIdDecode(input.right)); + if (message.method === "host.subscribeProfileEvents") { + client.profileEvents = "enabled" in params && params.enabled === true; + reply(socket, result(message.id, { enabled: client.profileEvents })); + return; } -}; -var fiberIdEncode = (input) => { - switch (input._tag) { - case "None": return { _tag: "None" }; - case "Runtime": return { - _tag: "Runtime", - id: input.id, - startTimeMillis: input.startTimeMillis - }; - case "Composite": return { - _tag: "Composite", - left: fiberIdEncode(input.left), - right: fiberIdEncode(input.right) - }; + if (message.method.startsWith("internal.") || message.method.startsWith("host.")) { + reply(socket, error(message.id, "Reserved method")); + return; } -}; -transform(FiberIdEncoded, FiberIdFromSelf, { - strict: true, - decode: (i) => fiberIdDecode(i), - encode: (a) => fiberIdEncode(a) -}).annotations({ identifier: "FiberId" }); -transform(Unknown, Unknown, { - strict: true, - decode: (i) => { - if (require_NodeRuntime.isObject(i) && "message" in i && typeof i.message === "string") { - const err = new Error(i.message, { cause: i }); - if ("name" in i && typeof i.name === "string") err.name = i.name; - err.stack = "stack" in i && typeof i.stack === "string" ? i.stack : ""; - return err; - } - return require_NodeRuntime.prettyErrorMessage(i); - }, - encode: (a) => { - if (a instanceof Error) return { - name: a.name, - message: a.message - }; - return require_NodeRuntime.prettyErrorMessage(a); + if (extensionProtocol !== "ready") { + reply(socket, message.method === "privateFill" ? result(message.id, { + status: extensionProtocol === "checking" ? "not-ready" : "unsupported", + retry: false + }) : error(message.id, "Extension protocol v2 is not ready; no action dispatched")); + return; } -}).annotations({ identifier: "Defect" }); -transform(Unknown, Boolean$, { - strict: true, - decode: (i) => require_NodeRuntime.isTruthy(i), - encode: require_NodeRuntime.identity -}).annotations({ identifier: "BooleanFromUnknown" }); -transform(Literal("true", "false").annotations({ description: "a string to be decoded into a boolean" }), Boolean$, { - strict: true, - decode: (i) => i === "true", - encode: (a) => a ? "true" : "false" -}).annotations({ identifier: "BooleanFromString" }); -var SymbolStruct = /* @__PURE__ */ TaggedStruct("symbol", { key: String$ }).annotations({ description: "an object to be decoded into a globally shared symbol" }); -var SymbolFromStruct = /* @__PURE__ */ transformOrFail(SymbolStruct, SymbolFromSelf, { - strict: true, - decode: (i) => decodeSymbol(i.key), - encode: (a, _, ast) => map(encodeSymbol(a, ast), (key) => SymbolStruct.make({ key })) -}); -/** @ignore */ -var PropertyKey$ = class extends Union(String$, Number$, SymbolFromStruct).annotations({ identifier: "PropertyKey" }) {}; -Struct({ - _tag: propertySignature(Literal("Pointer", "Unexpected", "Missing", "Composite", "Refinement", "Transformation", "Type", "Forbidden")).annotations({ description: "The tag identifying the type of parse issue" }), - path: propertySignature(Array$(PropertyKey$)).annotations({ description: "The path to the property where the issue occurred" }), - message: propertySignature(String$).annotations({ description: "A descriptive message explaining the issue" }) -}).annotations({ - identifier: "ArrayFormatterIssue", - description: "Represents an issue returned by the ArrayFormatter formatter" -}); -//#endregion -//#region src/shared/rpc.ts -var JsonRpcErrorSchema = Struct({ - code: Number$, - message: String$ -}); -var JsonRpcMessageSchema = Union(Struct({ - jsonrpc: Literal("2.0"), - id: optional(Union(Number$, String$, Null)), - method: String$, - params: optional(Unknown) -}), Struct({ - jsonrpc: Literal("2.0"), - id: Union(Number$, String$, Null), - result: optional(Unknown), - error: optional(JsonRpcErrorSchema) -})); -function parseJsonRpcMessage(value) { - const decoded = decodeUnknownEither(JsonRpcMessageSchema)(value); - if (require_NodeRuntime.isLeft(decoded)) throw new Error("Invalid JSON-RPC 2.0 message"); - return decoded.right; -} -function isJsonRpcRequest(message) { - return "method" in message; -} -//#endregion -//#region src/native-host/host.ts -var DEFAULT_PORT = Number(node_process.default.env.OPZERO_CHROME_HOST_PORT || 17365); -var SOCKET_PATH = node_process.default.env.OPZERO_CHROME_HOST_SOCKET || node_path.default.join(node_os.default.tmpdir(), "opzero-chrome-native-host.sock"); -var USE_TCP = node_process.default.platform === "win32" || node_process.default.env.OPZERO_CHROME_HOST_TRANSPORT === "tcp"; -var nextExtensionId = 1; -var pendingExtensionRequests = /* @__PURE__ */ new Map(); -var clients = /* @__PURE__ */ new Set(); -var HostIo = class extends require_NodeRuntime.Tag("opzero/NativeHostIo")() {}; -var HostIoLive = require_NodeRuntime.succeed(HostIo, { - log: (message) => require_NodeRuntime.sync(() => { - if (node_process.default.env.OPZERO_CHROME_HOST_DEBUG) node_process.default.stderr.write(`[opzero-chrome-host] ${message}\n`); - }), - writeNativeMessage: (message) => require_NodeRuntime.sync(() => { - const body = Buffer.from(JSON.stringify(message), "utf8"); - const header = Buffer.alloc(4); - header.writeUInt32LE(body.length, 0); - node_process.default.stdout.write(Buffer.concat([header, body])); - }), - writeClientLine: (client, message) => require_NodeRuntime.sync(() => { - client.write(`${JSON.stringify(message)}\n`); - }) -}); -function runHostEffect(program) { - return require_NodeRuntime.runPromise(require_NodeRuntime.provide(program, HostIoLive)); -} -function readNativeMessages(onMessage) { - let buffer = Buffer.alloc(0); - node_process.default.stdin.on("data", (chunk) => { - buffer = Buffer.concat([buffer, Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)]); - while (buffer.length >= 4) { - const length = buffer.readUInt32LE(0); - if (buffer.length < 4 + length) break; - const body = buffer.subarray(4, 4 + length); - buffer = buffer.subarray(4 + length); - try { - onMessage(parseJsonRpcMessage(JSON.parse(body.toString("utf8")))); - } catch (error) { - runHostEffect(require_NodeRuntime.flatMap(HostIo, (io) => io.log(`invalid native message: ${error instanceof Error ? error.message : String(error)}`))); - } - } + const extensionId = nextId++; + const timer = setTimeout(() => { + pending.delete(extensionId); + reply(socket, error(message.id, "Outcome unknown; connection revoked; do not replay")); + release(socket); + socket.end(); + }, Number(node_process.default.env.OPZERO_CHROME_REQUEST_TIMEOUT_MS || 3e4)); + pending.set(extensionId, { + socket, + id: message.id, + private: message.method === "privateFill", + timer }); -} -function rpcResult(id, result) { - return { + if (!native({ jsonrpc: "2.0", - id, - result - }; -} -function rpcError(id, error) { - const message = error instanceof Error ? error.message : String(error || "Unknown error"); - return { - jsonrpc: "2.0", - id, - error: { - code: typeof error === "object" && error != null && "code" in error && Number.isInteger(error.code) ? error.code : -32e3, - message - } - }; -} -function handleExtensionRequest(message) { - return require_NodeRuntime.gen(function* () { - const io = yield* HostIo; - if (!isJsonRpcRequest(message)) return; - if (message.method === "ping") return yield* io.writeNativeMessage(rpcResult(message.id, "pong")); - if (message.method === "getHostInfo") return yield* io.writeNativeMessage(rpcResult(message.id, { - name: "opzero-chrome-native-host", - version: "0.1.2", - pid: node_process.default.pid, - transport: USE_TCP ? "tcp" : "unix", - endpoint: USE_TCP ? `127.0.0.1:${DEFAULT_PORT}` : SOCKET_PATH - })); - return yield* io.writeNativeMessage(rpcError(message.id, /* @__PURE__ */ new Error(`Unsupported native host method: ${message.method}`))); - }); -} -function broadcast(message) { - return require_NodeRuntime.gen(function* () { - const io = yield* HostIo; - for (const client of clients) yield* io.writeClientLine(client, message); + id: extensionId, + method: message.method, + params: { + ...params, + session_id: client.session, + sessionId: client.session, + turn_id: epoch, + turnId: epoch + } + })) { + clearTimeout(timer); + pending.delete(extensionId); + reply(socket, error(message.id, "Request exceeds native frame limit; no action dispatched")); + } +} +var server = node_net.default.createServer((socket) => { + clients.set(socket, { + session: (0, node_crypto.randomUUID)(), + authenticated: !useTcp, + ids: /* @__PURE__ */ new Set(), + profileEvents: false }); -} -function handleNativeMessage(message) { - return require_NodeRuntime.gen(function* () { - const io = yield* HostIo; - if ("id" in message && ("result" in message || "error" in message)) { - if (message.id == null) return; - const pending = pendingExtensionRequests.get(message.id); - if (!pending) return; - pendingExtensionRequests.delete(message.id); - clearTimeout(pending.timer); - const response = message.error ? rpcError(pending.clientId, new Error(message.error.message)) : rpcResult(pending.clientId, message.result); - yield* io.writeClientLine(pending.client, response); - return; - } - if (isJsonRpcRequest(message) && "id" in message) { - yield* handleExtensionRequest(message); - return; - } - if (isJsonRpcRequest(message)) yield* broadcast(message); + socket.on("close", () => release(socket)); + socket.on("error", () => { + release(socket); + socket.destroy(); }); -} -function parseClientLines(socket, onMessage) { - let text = ""; socket.setEncoding("utf8"); + let text = ""; socket.on("data", (chunk) => { text += chunk; + if (Buffer.byteLength(text) > maxBytes) { + socket.destroy(); + return; + } let index; while ((index = text.indexOf("\n")) !== -1) { - const line = text.slice(0, index).trim(); + const line = text.slice(0, index); text = text.slice(index + 1); - if (!line) continue; + if (!line.trim()) continue; try { - onMessage(parseJsonRpcMessage(JSON.parse(line))); - } catch (error) { - const message = error instanceof Error ? error.message : String(error); - runHostEffect(require_NodeRuntime.flatMap(HostIo, (io) => io.writeClientLine(socket, rpcError(null, /* @__PURE__ */ new Error(`Invalid JSON: ${message}`))))); + handleClient(socket, require_rpc.parseJsonRpcMessage(JSON.parse(line))); + } catch { + reply(socket, error(null, "Invalid JSON-RPC request")); } } }); -} -function attachClient(socket) { - clients.add(socket); - socket.on("close", () => clients.delete(socket)); - socket.on("error", () => clients.delete(socket)); - parseClientLines(socket, (message) => { - runHostEffect(handleClientMessage(socket, message)); - }); -} -function handleClientMessage(socket, message) { - return require_NodeRuntime.gen(function* () { - const io = yield* HostIo; - if (!isJsonRpcRequest(message) || !("id" in message)) { - yield* io.writeClientLine(socket, rpcError("id" in message ? message.id : null, /* @__PURE__ */ new Error("Expected JSON-RPC request with id"))); - return; - } - if (message.method === "host.ping") { - yield* io.writeClientLine(socket, rpcResult(message.id, "pong")); - return; - } - const extensionId = nextExtensionId++; - const timer = setTimeout(() => { - pendingExtensionRequests.delete(extensionId); - runHostEffect(require_NodeRuntime.flatMap(HostIo, (io) => io.writeClientLine(socket, rpcError(message.id, /* @__PURE__ */ new Error(`Extension request ${message.method} timed out`))))); - }, Number(node_process.default.env.OPZERO_CHROME_REQUEST_TIMEOUT_MS || 3e4)); - pendingExtensionRequests.set(extensionId, { - client: socket, - clientId: message.id ?? null, - timer +}); +function shutdown(code) { + for (const socket of clients.keys()) socket.destroy(); + if (ownsSocket) try { + node_fs.default.unlinkSync(socketPath); + } catch {} + node_process.default.exit(code); +} +server.on("error", () => { + node_process.default.stderr.write("Native endpoint unavailable; no existing endpoint was replaced\n"); + shutdown(1); +}); +try { + if (useTcp) { + const file = node_process.default.env.OPZERO_CHROME_HOST_TOKEN_FILE; + if (!file) throw new Error("token file required"); + const stat = node_fs.default.lstatSync(file); + if (!stat.isFile() || node_process.default.platform !== "win32" && (stat.mode & 63) !== 0) throw new Error("private token file required"); + tcpToken = Buffer.from(node_fs.default.readFileSync(file, "utf8").trim()); + if (tcpToken.length < 32) throw new Error("token too short"); + server.listen(port, "127.0.0.1"); + } else { + node_process.default.umask(63); + const directory = node_path.default.dirname(socketPath); + node_fs.default.mkdirSync(directory, { + recursive: true, + mode: 448 }); - yield* io.writeNativeMessage({ - jsonrpc: "2.0", - id: extensionId, - method: message.method, - params: message.params || {} + const stat = node_fs.default.lstatSync(directory); + if (!stat.isDirectory() || stat.uid !== node_process.default.getuid?.() || (stat.mode & 63) !== 0) throw new Error("private socket directory required"); + if (node_fs.default.existsSync(socketPath)) throw new Error("endpoint exists"); + server.listen(socketPath, () => { + ownsSocket = true; + node_fs.default.chmodSync(socketPath, 384); }); - }); -} -function startServer() { - const server = node_net.default.createServer(attachClient); - server.on("error", (error) => { - runHostEffect(require_NodeRuntime.flatMap(HostIo, (io) => io.log(`server error: ${error.message}`))); - node_process.default.exitCode = 1; - }); - if (USE_TCP) { - server.listen(DEFAULT_PORT, "127.0.0.1", () => runHostEffect(require_NodeRuntime.flatMap(HostIo, (io) => io.log(`listening on 127.0.0.1:${DEFAULT_PORT}`)))); - return; } - try { - if (node_fs.default.existsSync(SOCKET_PATH)) node_fs.default.unlinkSync(SOCKET_PATH); - server.listen(SOCKET_PATH, () => runHostEffect(require_NodeRuntime.flatMap(HostIo, (io) => io.log(`listening on ${SOCKET_PATH}`)))); - } catch (error) { - runHostEffect(require_NodeRuntime.flatMap(HostIo, (io) => io.log(`socket setup failed: ${error instanceof Error ? error.message : String(error)}`))); - } -} -require_NodeRuntime.runMain(require_NodeRuntime.gen(function* () { - node_process.default.stdin.on("end", () => node_process.default.exit(0)); - node_process.default.stdin.on("error", () => node_process.default.exit(1)); - readNativeMessages((message) => runHostEffect(handleNativeMessage(message))); - startServer(); - yield* require_NodeRuntime.never; -}).pipe(require_NodeRuntime.provide(HostIoLive))); +} catch { + node_process.default.stderr.write("Native endpoint setup refused; use a private directory or authenticated TCP\n"); + shutdown(1); +} +var buffer = Buffer.alloc(0); +node_process.default.stdin.on("data", (chunk) => { + buffer = Buffer.concat([buffer, Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)]); + while (buffer.length >= 4) { + const length = buffer.readUInt32LE(0); + if (length > maxResponseBytes) shutdown(1); + if (buffer.length < length + 4) break; + const body = buffer.subarray(4, length + 4); + buffer = buffer.subarray(length + 4); + try { + handleNative(require_rpc.parseJsonRpcMessage(JSON.parse(body.toString("utf8")))); + } catch {} + } +}); +node_process.default.stdin.on("end", () => shutdown(0)); +node_process.default.stdin.on("error", () => shutdown(1)); +node_process.default.stdout.on("error", () => shutdown(1)); +node_process.default.on("SIGTERM", () => shutdown(0)); +node_process.default.on("SIGINT", () => shutdown(0)); +native({ + jsonrpc: "2.0", + id: protocolRequestId, + method: "getInfo", + params: {} +}); //#endregion diff --git a/skills/chrome-control/native-host/transport.js b/skills/chrome-control/native-host/transport.js new file mode 100644 index 0000000..c00bfea --- /dev/null +++ b/skills/chrome-control/native-host/transport.js @@ -0,0 +1,419 @@ +Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" }); +const require_Layer = require("../chunks/Layer-nAKmzBoW.js"); +const require_rpc = require("../chunks/rpc-BTiTvO3J.js"); +let node_net = require("node:net"); +node_net = require_Layer.__toESM(node_net); +let node_path = require("node:path"); +node_path = require_Layer.__toESM(node_path); +let node_crypto = require("node:crypto"); +let node_fs_promises = require("node:fs/promises"); +node_fs_promises = require_Layer.__toESM(node_fs_promises); +let node_child_process = require("node:child_process"); +let node_util = require("node:util"); +//#region src/shared/page-protocol.ts +function parseObservation(value) { + const fail = () => { + throw new Error("Invalid page protocol v2 observation"); + }; + const object = (v) => typeof v === "object" && v !== null && !Array.isArray(v) ? v : fail(); + const string = (v, limit) => typeof v === "string" && v.length <= limit ? v : fail(); + const bool = (v) => typeof v === "boolean" ? v : fail(); + const raw = object(value), truncation = object(raw.truncation); + if (raw.status !== "observed" || raw.pageProtocolVersion !== 2 || raw.mode !== "full" && raw.mode !== "controls-only" || !Array.isArray(raw.actions) || raw.actions.length > 100 || !Array.isArray(raw.opaqueSurfaces) || raw.opaqueSurfaces.length > 100) fail(); + const actions = raw.actions.map((value) => { + const a = object(value); + if (a.kind !== "fill" && a.kind !== "click") return fail(); + return { + id: string(a.id, 100), + kind: a.kind, + label: string(a.label, 160), + role: string(a.role, 80), + disabled: bool(a.disabled) + }; + }); + const opaqueSurfaces = raw.opaqueSurfaces.map((value, index) => { + const o = object(value); + if (Object.keys(o).length !== 2 || o.id !== `opaque-${index}` || typeof o.kind !== "string" || ![ + "iframe", + "frame", + "object", + "embed", + "closed-shadow-root" + ].includes(o.kind)) return fail(); + return { + id: string(o.id, 100), + kind: o.kind + }; + }); + const flags = { + text: bool(truncation.text), + actions: bool(truncation.actions), + opaqueSurfaces: bool(truncation.opaqueSurfaces), + labels: bool(truncation.labels), + title: bool(truncation.title) + }; + const text = string(raw.text, 12e3), snapshot = string(raw.snapshot, 200), partial = bool(raw.partial); + if (!snapshot || actions.some((a) => !a.id) || new Set(actions.map((a) => a.id)).size !== actions.length || partial !== opaqueSurfaces.length > 0 || flags.opaqueSurfaces && opaqueSurfaces.length !== 100 || raw.mode === "controls-only" && (text !== "" || flags.text)) fail(); + return { + status: "observed", + pageProtocolVersion: 2, + snapshot, + url: string(raw.url, 8192), + title: string(raw.title, 200), + text, + actions, + mode: raw.mode, + partial, + opaqueSurfaces, + truncation: flags + }; +} +//#endregion +//#region src/native-host/transport.ts +var exec = (0, node_util.promisify)(node_child_process.execFile); +var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); +function object(value) { + if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error("Invalid transport response"); + return value; +} +function string(value) { + if (typeof value !== "string") throw new Error("Invalid string response"); + return value; +} +var ChromeTransport = class ChromeTransport { + #socket; + #next = 0; + #pending = /* @__PURE__ */ new Map(); + #pages = /* @__PURE__ */ new Map(); + #recordings = /* @__PURE__ */ new Map(); + session; + epoch; + constructor(socket, session, epoch) { + this.#socket = socket; + this.session = session; + this.epoch = epoch; + } + static async connect(socketPath) { + const directory = await node_fs_promises.default.lstat(node_path.default.dirname(socketPath)); + const endpoint = await node_fs_promises.default.lstat(socketPath); + if (!directory.isDirectory() || directory.uid !== process.getuid?.() || (directory.mode & 63) !== 0 || !endpoint.isSocket() || endpoint.uid !== process.getuid?.()) throw new Error("Explicit private owned Unix socket required"); + const socket = node_net.default.createConnection(socketPath); + await new Promise((resolve, reject) => { + socket.once("connect", resolve); + socket.once("error", reject); + }); + const transport = new ChromeTransport(socket, "", ""); + let text = ""; + socket.setEncoding("utf8"); + socket.on("data", (chunk) => { + text += chunk; + if (Buffer.byteLength(text) > 64 * 1024 * 1024) { + transport.#fail(); + return; + } + let index; + while ((index = text.indexOf("\n")) >= 0) { + const line = text.slice(0, index); + text = text.slice(index + 1); + try { + const response = require_rpc.parseJsonRpcMessage(JSON.parse(line)); + if (require_rpc.isJsonRpcRequest(response) || typeof response.id !== "number") continue; + const pending = transport.#pending.get(response.id); + if (!pending) continue; + clearTimeout(pending.timer); + transport.#pending.delete(response.id); + if (response.error) pending.reject(new Error(response.error.message)); + else pending.resolve(response.result); + } catch { + transport.#fail(); + } + } + }); + socket.on("error", () => transport.#fail()); + socket.on("close", () => transport.#fail()); + try { + const info = object(await transport.#call("host.info")); + if (info.protocolVersion !== 2 || info.extensionProtocol !== "ready") throw new Error("Extension protocol not ready; connect again explicitly without replaying inputs"); + const extension = object(await transport.#call("getInfo")); + if (extension.protocolVersion !== 2 || extension.pageProtocolVersion !== 2) throw new Error("Typed page protocol version 2 required"); + Object.defineProperties(transport, { + session: { value: string(info.session_id) }, + epoch: { value: string(info.epoch) } + }); + return transport; + } catch (error) { + transport.#fail(); + throw error; + } + } + #fail() { + this.#socket.destroy(); + this.#pages.clear(); + for (const request of this.#pending.values()) { + clearTimeout(request.timer); + request.reject(/* @__PURE__ */ new Error("Transport closed; outcome unknown; no replay")); + } + this.#pending.clear(); + } + #call(method, params = {}) { + if (this.#socket.destroyed) return Promise.reject(/* @__PURE__ */ new Error("Transport closed; no replay")); + const id = ++this.#next; + return new Promise((resolve, reject) => { + const timer = setTimeout(() => this.#fail(), 35e3); + this.#pending.set(id, { + resolve, + reject, + timer + }); + this.#socket.write(JSON.stringify({ + jsonrpc: "2.0", + id, + method, + params + }) + "\n"); + }); + } + #owned(page) { + if (this.#pages.get(page.tabId) !== page || this.#socket.destroyed) throw new Error("Page is not owned by this connection"); + return { tabId: page.tabId }; + } + async open(url, options = {}) { + const origin = new URL(url).origin; + const result = object(await this.#call("createTab")); + if (typeof result.id !== "number" || result.active !== false) throw new Error("Inactive page creation failed"); + const page = Object.freeze({ + tabId: result.id, + origin + }); + this.#pages.set(page.tabId, page); + await this.#call("attach", { tabId: page.tabId }); + await this.#call("bindPage", { + tabId: page.tabId, + expectedOrigin: origin, + ...options + }); + await this.navigate(page, url); + return page; + } + async navigate(page, url) { + if (new URL(url).origin !== page.origin) throw new Error("Origin change refused"); + await this.#call("navigatePage", { + ...this.#owned(page), + url + }); + } + async observe(page, options = {}) { + if (options.controlsOnly !== void 0 && typeof options.controlsOnly !== "boolean") throw new Error("Invalid observation mode"); + const result = parseObservation(await this.#call("observePage", { + ...this.#owned(page), + controlsOnly: options.controlsOnly === true + })); + if (new URL(result.url).origin !== page.origin || result.mode !== (options.controlsOnly ? "controls-only" : "full")) throw new Error("Observation binding changed"); + return result; + } + async waitFor(page, expect, timeoutMs = 1e4) { + if (!expect.text && !expect.url || timeoutMs < 1 || timeoutMs > 15e3) throw new Error("Bounded nonempty expectation required"); + const deadline = performance.now() + timeoutMs; + while (true) { + try { + const snapshot = await this.observe(page); + if ((!expect.text || snapshot.text.includes(expect.text)) && (!expect.url || snapshot.url === expect.url)) return snapshot; + } catch (error) { + if (!(error instanceof Error) || ![ + "Page origin not ready or mismatch", + "Frame with ID 0 was removed.", + "Capture or observation blocked: private fields or frames", + "Private document quarantined until cross-document navigation" + ].includes(error.message)) throw error; + } + if (performance.now() >= deadline) throw new Error("Read-only wait timed out; no input replayed"); + await sleep(50); + } + } + async act(page, snapshot, actionId, text) { + const result = object(await this.#call("actPage", { + ...this.#owned(page), + snapshot, + actionId, + ...text === void 0 ? {} : { text } + })); + if (result.status !== "executed" && result.status !== "not-executed" && result.status !== "unknown") throw new Error("Invalid action result; no replay"); + return { + status: result.status, + retry: false, + ...typeof result.reason === "string" ? { reason: result.reason } : {} + }; + } + async privateFill(page, selectors, values, options = {}) { + if (this.#recordings.has(page.tabId)) throw new Error("Stop recording before private input"); + const params = this.#owned(page); + const observed = object(await this.#call("observeDocument", { + ...params, + expectedOrigin: page.origin, + selectors, + ...options + })); + return { + status: object(await this.#call("privateFill", { + ...params, + expectedOrigin: page.origin, + token: string(observed.token), + documentId: string(observed.documentId), + values + })).status === "filled" ? "filled" : "not-filled-or-unknown", + retry: false + }; + } + async preparePrivateSubmit(page, snapshot, actionId) { + const result = object(await this.#call("preparePrivateSubmit", { + ...this.#owned(page), + snapshot, + actionId + })); + if (result.status !== "prepared") throw new Error("Private submit preparation refused"); + return Object.freeze({ + submitToken: string(result.submitToken), + documentId: string(result.documentId) + }); + } + async submitPrivate(page, submission) { + const result = object(await this.#call("submitPrivate", { + ...this.#owned(page), + submitToken: submission.submitToken, + documentId: submission.documentId + })); + if (result.status !== "executed" && result.status !== "not-executed" && result.status !== "unknown") throw new Error("Invalid private submit result; no replay"); + return { + status: result.status, + retry: false + }; + } + async screenshot(page) { + const result = object(await this.#call("capturePage", this.#owned(page))); + const data = Buffer.from(string(result.data), "base64"); + if (data.subarray(0, 3).toString("hex") !== "ffd8ff") throw new Error("Invalid JPEG response"); + return data; + } + async startRecording(page, artifactRoot, options = {}) { + const fps = options.fps ?? 5, maxSeconds = options.maxSeconds ?? 30; + if (!Number.isInteger(fps) || fps < 1 || fps > 15 || !Number.isFinite(maxSeconds) || maxSeconds < 1 || maxSeconds > 60 || this.#recordings.has(page.tabId)) throw new Error("Invalid or duplicate recording"); + const stat = await node_fs_promises.default.lstat(artifactRoot); + if (!stat.isDirectory() || stat.uid !== process.getuid?.() || (stat.mode & 63) !== 0) throw new Error("Owned private artifact directory required"); + const directory = await node_fs_promises.default.mkdtemp(node_path.default.join(artifactRoot, "tab-video-")); + await node_fs_promises.default.chmod(directory, 448); + await this.#call("recordingState", { + ...this.#owned(page), + active: true + }); + const frames = []; + const start = performance.now(); + let stopped = false, error = null, bytes = 0, ended = start, captureMs = 0; + const capture = async () => { + const began = performance.now(); + try { + const data = await this.screenshot(page); + bytes += data.length; + if (bytes > 100 * 1024 * 1024) throw new Error("Recording storage limit"); + const file = `${String(frames.length).padStart(5, "0")}.jpg`; + await node_fs_promises.default.writeFile(node_path.default.join(directory, file), data, { mode: 384 }); + frames.push({ + file, + seconds: (performance.now() - start) / 1e3, + sha256: (0, node_crypto.createHash)("sha256").update(data).digest("hex") + }); + } finally { + captureMs += performance.now() - began; + } + }; + let stopPromise; + const record = { stop: () => stopPromise ??= (async () => { + stopped = true; + await loop; + try { + await this.#call("recordingState", { + ...this.#owned(page), + active: false + }); + } catch { + error ??= "Transport unavailable; recording incomplete"; + } + this.#recordings.delete(page.tabId); + const seconds = Math.max(1 / 30, (ended - start) / 1e3 - (frames[0]?.seconds ?? 0)); + const encodeStart = performance.now(); + let output = null; + if (frames.length) { + const concat = frames.map((frame, i) => `file '${frame.file}'\nduration ${Math.max(.001, (frames[i + 1]?.seconds ?? (ended - start) / 1e3) - frame.seconds).toFixed(6)}`).join("\n") + `\nfile '${frames.at(-1)?.file}'\n`; + await node_fs_promises.default.writeFile(node_path.default.join(directory, "frames.ffconcat"), concat, { mode: 384 }); + try { + await exec("ffmpeg", [ + "-v", + "error", + "-y", + "-f", + "concat", + "-safe", + "1", + "-i", + "frames.ffconcat", + "-vf", + "fps=30,pad=ceil(iw/2)*2:ceil(ih/2)*2", + "-t", + String(seconds), + "-c:v", + "libx264", + "-pix_fmt", + "yuv420p", + "-movflags", + "+faststart", + "recording.mp4" + ], { + cwd: directory, + timeout: 6e4, + env: { PATH: process.env.PATH } + }); + output = node_path.default.join(directory, "recording.mp4"); + await node_fs_promises.default.chmod(output, 384); + } catch { + error ??= "Encoding failed; JPEG frames preserved"; + } + } + const receipt = { + path: output, + directory, + seconds, + frames, + error, + encodeMs: performance.now() - encodeStart, + captureMs, + sampleFps: fps + }; + await node_fs_promises.default.writeFile(node_path.default.join(directory, "capture.json"), JSON.stringify(receipt, null, 2), { mode: 384 }); + return receipt; + })() }; + const loop = (async () => { + try { + do { + await capture(); + if (stopped) break; + await sleep(1e3 / fps); + } while (!stopped && performance.now() - start < maxSeconds * 1e3); + } catch { + error = "Capture interrupted: privacy, ownership, navigation, or transport changed"; + } finally { + ended = performance.now(); + } + })(); + this.#recordings.set(page.tabId, record); + return record; + } + async close() { + try { + for (const recording of this.#recordings.values()) await recording.stop(); + if (!this.#socket.destroyed) await this.#call("finalizeTabs", { keep: [] }); + } finally { + this.#fail(); + } + } +}; +//#endregion +exports.ChromeTransport = ChromeTransport; diff --git a/skills/chrome-control/scripts/check-extension-installed.js b/skills/chrome-control/scripts/check-extension-installed.js index 932fd7f..1e6bb21 100644 --- a/skills/chrome-control/scripts/check-extension-installed.js +++ b/skills/chrome-control/scripts/check-extension-installed.js @@ -1,14 +1,14 @@ #!/usr/bin/env node -const require_NodeRuntime = require("../chunks/NodeRuntime-CfddDOOs.js"); -const require_effect_services = require("../chunks/effect-services-BrlJlqgg.js"); +const require_Layer = require("../chunks/Layer-nAKmzBoW.js"); +const require_effect_services = require("../chunks/effect-services-BVazUC0E.js"); let node_os = require("node:os"); -node_os = require_NodeRuntime.__toESM(node_os); +node_os = require_Layer.__toESM(node_os); let node_path = require("node:path"); -node_path = require_NodeRuntime.__toESM(node_path); +node_path = require_Layer.__toESM(node_path); //#region src/scripts/check-extension-installed.ts var json = process.argv.includes("--json"); function output(result, exitCode) { - return require_NodeRuntime.gen(function* () { + return require_Layer.gen(function* () { const io = yield* require_effect_services.ScriptIo; if (json) yield* io.stdout(`${JSON.stringify(result, null, 2)}\n`); else if (result.ok) yield* io.stdout(`${result.message}\n`); @@ -17,7 +17,7 @@ function output(result, exitCode) { }); } function configuredExtensionId() { - return require_NodeRuntime.gen(function* () { + return require_Layer.gen(function* () { const io = yield* require_effect_services.ScriptIo; const explicit = require_effect_services.argValue("extension-id", process.argv.find((arg) => !arg.startsWith("--") && arg !== process.argv[0] && arg !== process.argv[1])); if (explicit) return explicit; @@ -28,11 +28,11 @@ function configuredExtensionId() { }); } function selectProfilePreferences(userDataDir) { - return require_NodeRuntime.gen(function* () { + return require_Layer.gen(function* () { const io = yield* require_effect_services.ScriptIo; const localStatePath = node_path.default.join(userDataDir, "Local State"); if (yield* io.exists(localStatePath)) { - const parsed = yield* require_NodeRuntime.either(io.readText(localStatePath)); + const parsed = yield* require_Layer.either(io.readText(localStatePath)); if (parsed._tag === "Right") try { const lastProfile = JSON.parse(parsed.right).profile?.last_used; if (lastProfile && (yield* io.exists(node_path.default.join(userDataDir, lastProfile, "Preferences")))) return node_path.default.join(userDataDir, lastProfile, "Preferences"); @@ -51,18 +51,18 @@ function selectProfilePreferences(userDataDir) { }); } function preferencesPath() { - return require_NodeRuntime.gen(function* () { + return require_Layer.gen(function* () { if (process.env.OPZERO_CHROME_PREFERENCES_PATH) return process.env.OPZERO_CHROME_PREFERENCES_PATH; if (process.env.OPZERO_CHROME_USER_DATA_DIR) return yield* selectProfilePreferences(process.env.OPZERO_CHROME_USER_DATA_DIR); if (process.env.CHROME_PROFILE_DIR) return node_path.default.join(process.env.CHROME_PROFILE_DIR, "Preferences"); if (process.platform === "darwin") return yield* selectProfilePreferences(node_path.default.join(node_os.default.homedir(), "Library", "Application Support", "Google", "Chrome")); if (process.platform === "linux") return yield* selectProfilePreferences(node_path.default.join(node_os.default.homedir(), ".config", "google-chrome")); if (process.platform === "win32") return yield* selectProfilePreferences(node_path.default.join(node_os.default.homedir(), "AppData", "Local", "Google", "Chrome", "User Data")); - return yield* require_NodeRuntime.fail(/* @__PURE__ */ new Error(`Unsupported platform: ${process.platform}`)); + return yield* require_Layer.fail(/* @__PURE__ */ new Error(`Unsupported platform: ${process.platform}`)); }); } function readExtensionSettings(profilePreferencesPath, extensionId) { - return require_NodeRuntime.gen(function* () { + return require_Layer.gen(function* () { const io = yield* require_effect_services.ScriptIo; const paths = [profilePreferencesPath, node_path.default.join(node_path.default.dirname(profilePreferencesPath), "Secure Preferences")]; for (const settingsPath of paths) { @@ -76,7 +76,7 @@ function readExtensionSettings(profilePreferencesPath, extensionId) { return null; }); } -require_effect_services.runScript(require_NodeRuntime.gen(function* () { +require_effect_services.runScript(require_Layer.gen(function* () { const io = yield* require_effect_services.ScriptIo; const extensionId = yield* configuredExtensionId(); if (!extensionId) { diff --git a/skills/chrome-control/scripts/check-native-host-manifest.js b/skills/chrome-control/scripts/check-native-host-manifest.js index 27f370b..c51fc68 100644 --- a/skills/chrome-control/scripts/check-native-host-manifest.js +++ b/skills/chrome-control/scripts/check-native-host-manifest.js @@ -1,10 +1,10 @@ #!/usr/bin/env node -const require_NodeRuntime = require("../chunks/NodeRuntime-CfddDOOs.js"); -const require_effect_services = require("../chunks/effect-services-BrlJlqgg.js"); +const require_Layer = require("../chunks/Layer-nAKmzBoW.js"); +const require_effect_services = require("../chunks/effect-services-BVazUC0E.js"); let node_os = require("node:os"); -node_os = require_NodeRuntime.__toESM(node_os); +node_os = require_Layer.__toESM(node_os); let node_path = require("node:path"); -node_path = require_NodeRuntime.__toESM(node_path); +node_path = require_Layer.__toESM(node_path); //#region src/scripts/check-native-host-manifest.ts var hostName = "com.opzero.chrome"; var json = process.argv.includes("--json"); @@ -16,7 +16,7 @@ function defaultManifestPath() { } var manifestPath = require_effect_services.argValue("manifest-path", defaultManifestPath()); function configuredExtensionId() { - return require_NodeRuntime.gen(function* () { + return require_Layer.gen(function* () { const io = yield* require_effect_services.ScriptIo; const explicit = require_effect_services.argValue("extension-id", process.env.OPZERO_CHROME_EXTENSION_ID); if (explicit) return explicit; @@ -26,11 +26,11 @@ function configuredExtensionId() { }); } function checkWindowsRegistry(expectedPath) { - return require_NodeRuntime.gen(function* () { + return require_Layer.gen(function* () { if (process.platform !== "win32") return []; const io = yield* require_effect_services.ScriptIo; const key = `HKCU\\Software\\Google\\Chrome\\NativeMessagingHosts\\${hostName}`; - const output = yield* require_NodeRuntime.either(io.execFile("reg", [ + const output = yield* require_Layer.either(io.execFile("reg", [ "query", key, "/ve" @@ -40,7 +40,7 @@ function checkWindowsRegistry(expectedPath) { }); } function output(result, exitCode) { - return require_NodeRuntime.gen(function* () { + return require_Layer.gen(function* () { const io = yield* require_effect_services.ScriptIo; if (json) yield* io.stdout(`${JSON.stringify(result, null, 2)}\n`); else if (result.ok) yield* io.stdout(`${result.message}\n`); @@ -58,7 +58,7 @@ function repairCommand(extensionId) { manifestPath ]; } -require_effect_services.runScript(require_NodeRuntime.gen(function* () { +require_effect_services.runScript(require_Layer.gen(function* () { const io = yield* require_effect_services.ScriptIo; const extensionId = yield* configuredExtensionId(); if (!extensionId) { diff --git a/skills/chrome-control/scripts/chrome-is-running.js b/skills/chrome-control/scripts/chrome-is-running.js index fc54c97..33ed5a2 100644 --- a/skills/chrome-control/scripts/chrome-is-running.js +++ b/skills/chrome-control/scripts/chrome-is-running.js @@ -1,12 +1,12 @@ #!/usr/bin/env node -const require_NodeRuntime = require("../chunks/NodeRuntime-CfddDOOs.js"); -const require_effect_services = require("../chunks/effect-services-BrlJlqgg.js"); +const require_Layer = require("../chunks/Layer-nAKmzBoW.js"); +const require_effect_services = require("../chunks/effect-services-BVazUC0E.js"); //#region src/scripts/chrome-is-running.ts var json = process.argv.includes("--json"); -require_effect_services.runScript(require_NodeRuntime.gen(function* () { +require_effect_services.runScript(require_Layer.gen(function* () { const io = yield* require_effect_services.ScriptIo; const [cmd, args] = process.platform === "win32" ? ["tasklist", []] : ["pgrep", ["-fl", "Google Chrome|Chromium"]]; - const output = yield* require_NodeRuntime.either(io.execFile(cmd, args, { encoding: "utf8" })); + const output = yield* require_Layer.either(io.execFile(cmd, args, { encoding: "utf8" })); const processList = output._tag === "Right" ? output.right : ""; const running = /chrome|chromium/i.test(processList); const result = { diff --git a/skills/chrome-control/scripts/install-native-host.js b/skills/chrome-control/scripts/install-native-host.js index d4fc5d0..a9f5027 100644 --- a/skills/chrome-control/scripts/install-native-host.js +++ b/skills/chrome-control/scripts/install-native-host.js @@ -1,12 +1,12 @@ #!/usr/bin/env node -const require_NodeRuntime = require("../chunks/NodeRuntime-CfddDOOs.js"); -const require_effect_services = require("../chunks/effect-services-BrlJlqgg.js"); +const require_Layer = require("../chunks/Layer-nAKmzBoW.js"); +const require_effect_services = require("../chunks/effect-services-BVazUC0E.js"); let node_os = require("node:os"); -node_os = require_NodeRuntime.__toESM(node_os); +node_os = require_Layer.__toESM(node_os); let node_path = require("node:path"); -node_path = require_NodeRuntime.__toESM(node_path); +node_path = require_Layer.__toESM(node_path); let node_process = require("node:process"); -node_process = require_NodeRuntime.__toESM(node_process); +node_process = require_Layer.__toESM(node_process); //#region src/scripts/install-native-host.ts var root = node_path.default.resolve(__dirname, ".."); var hostName = "com.opzero.chrome"; @@ -17,7 +17,7 @@ function chromeManifestPath() { throw new Error(`Unsupported platform: ${node_process.default.platform}`); } function registerWindowsManifest(manifestPath) { - return require_NodeRuntime.gen(function* () { + return require_Layer.gen(function* () { if (node_process.default.platform !== "win32") return; const io = yield* require_effect_services.ScriptIo; const key = `HKCU\\Software\\Google\\Chrome\\NativeMessagingHosts\\${hostName}`; @@ -60,7 +60,7 @@ function windowsNativeHostLauncher() { ${socketPath ? `set "OPZERO_CHROME_HOST_SOCKET=${socketPath.replace(/"/g, "\"\"")}"\r\n` : ""}"${node_process.default.execPath.replace(/"/g, "\"\"")}" "%~dp0host.js" `; } -require_effect_services.runScript(require_NodeRuntime.gen(function* () { +require_effect_services.runScript(require_Layer.gen(function* () { const io = yield* require_effect_services.ScriptIo; const extensionId = require_effect_services.argValue("extension-id", node_process.default.env.OPZERO_CHROME_EXTENSION_ID); if (!extensionId) { @@ -86,9 +86,9 @@ require_effect_services.runScript(require_NodeRuntime.gen(function* () { yield* io.stdout(`Installed native messaging manifest:\n${manifestPath}\n`); yield* io.stdout(`Allowed extension origin: chrome-extension://${extensionId}/\n`); yield* io.stdout(`Host executable: ${hostPath}\n`); - yield* require_NodeRuntime.catchAll(io.writeText(node_path.default.join(__dirname, "extension-id.json"), `${JSON.stringify({ + yield* require_Layer.catchAll(io.writeText(node_path.default.join(__dirname, "extension-id.json"), `${JSON.stringify({ extensionId, extensionHostName: hostName - }, null, 2)}\n`), () => require_NodeRuntime._void); + }, null, 2)}\n`), () => require_Layer._void); })); //#endregion diff --git a/skills/chrome-control/scripts/installed-browsers.js b/skills/chrome-control/scripts/installed-browsers.js index 519ec33..db2d501 100644 --- a/skills/chrome-control/scripts/installed-browsers.js +++ b/skills/chrome-control/scripts/installed-browsers.js @@ -1,12 +1,12 @@ #!/usr/bin/env node -const require_NodeRuntime = require("../chunks/NodeRuntime-CfddDOOs.js"); -const require_effect_services = require("../chunks/effect-services-BrlJlqgg.js"); +const require_Layer = require("../chunks/Layer-nAKmzBoW.js"); +const require_effect_services = require("../chunks/effect-services-BVazUC0E.js"); //#region src/scripts/installed-browsers.ts var json = process.argv.includes("--json"); function commandExists(command) { - return require_NodeRuntime.gen(function* () { + return require_Layer.gen(function* () { const io = yield* require_effect_services.ScriptIo; - return (yield* require_NodeRuntime.either(process.platform === "win32" ? io.execFile("where", [command], { + return (yield* require_Layer.either(process.platform === "win32" ? io.execFile("where", [command], { encoding: "utf8", stdio: "ignore" }) : io.execFile("sh", ["-c", `command -v ${command}`], { @@ -16,7 +16,7 @@ function commandExists(command) { }); } function detect() { - return require_NodeRuntime.gen(function* () { + return require_Layer.gen(function* () { const io = yield* require_effect_services.ScriptIo; if (process.platform === "darwin") { const chrome = yield* io.exists("/Applications/Google Chrome.app"); @@ -52,7 +52,7 @@ function detect() { return []; }); } -require_effect_services.runScript(require_NodeRuntime.gen(function* () { +require_effect_services.runScript(require_Layer.gen(function* () { const io = yield* require_effect_services.ScriptIo; const browsers = yield* detect(); const result = { diff --git a/skills/chrome-control/scripts/open-chrome-window.js b/skills/chrome-control/scripts/open-chrome-window.js index de85d05..a222c70 100644 --- a/skills/chrome-control/scripts/open-chrome-window.js +++ b/skills/chrome-control/scripts/open-chrome-window.js @@ -1,13 +1,13 @@ #!/usr/bin/env node -const require_NodeRuntime = require("../chunks/NodeRuntime-CfddDOOs.js"); -const require_effect_services = require("../chunks/effect-services-BrlJlqgg.js"); +const require_Layer = require("../chunks/Layer-nAKmzBoW.js"); +const require_effect_services = require("../chunks/effect-services-BVazUC0E.js"); //#region src/scripts/open-chrome-window.ts var json = process.argv.includes("--json"); var dryRun = process.argv.includes("--dry-run"); var url = process.argv.find((arg) => !arg.startsWith("--") && arg !== process.argv[0] && arg !== process.argv[1]) || "about:blank"; -require_effect_services.runScript(require_NodeRuntime.gen(function* () { +require_effect_services.runScript(require_Layer.gen(function* () { const io = yield* require_effect_services.ScriptIo; - const [command, args] = yield* require_NodeRuntime.try_({ + const [command, args] = yield* require_Layer.try_({ try: () => { if (process.platform === "darwin") return ["open", [ "-na", diff --git a/src/extension/background.ts b/src/extension/background.ts index 78032a7..90b19ed 100644 --- a/src/extension/background.ts +++ b/src/extension/background.ts @@ -1,6 +1,8 @@ import { Context, Effect, Either, Layer } from "effect"; import { parseJsonRpcMessage, type JsonRpcMessage, type JsonRpcRequest } from "../shared/rpc"; import { parseWithSchema, RuntimeMessageSchema, type CursorState, type NativeHostStatus, type RuntimeMessage } from "../shared/extension-schemas"; +import { observePrivateFields, fillPrivateFields } from "./private-input"; +import { pageControl, type PageOptions } from "./page-control"; const HOST_NAMES = [ "com.opzero.chrome", @@ -73,6 +75,14 @@ const commandQueues = new Map>(); const cursorStates = new Map(); const cursorWaiters = new Map(); const tabOrigins = new Map(); +const revokedSessions = new Set(); +const tabGenerations = new Map(); +const privateObservations = new Map(); +const pageOrigins = new Map(); +const pageOperations = new Map>(); +const pageSnapshots = new Map(); +const pageRecordings = new Set(); +const PRIVATE_CAPTURE_KEY = "PRIVATE_CAPTURE_QUARANTINE"; let deliverableGroupId: number | null = null; let nativeTransport: NativeTransport; @@ -163,6 +173,7 @@ function normalizeSessionParams(params: JsonRecord = {}) { function ensureSession(params: JsonRecord = {}): Session { const { sessionId, turnId } = normalizeSessionParams(params); + if (revokedSessions.has(sessionId)) throw createRpcError("Session revoked; reconnect requires a new session"); let session = sessions.get(sessionId); if (!session) { session = { @@ -318,6 +329,9 @@ function ensureDeliverableGroup(tabIds: number[]) { function registerSessionTab(session: Session, tab: chrome.tabs.Tab | { id: number }, origin: TabOrigin) { if (tab.id == null) throw createRpcError("Missing tab id"); + if (sessions.get(session.id) !== session || revokedSessions.has(session.id)) throw createRpcError("Session revoked"); + const owner = tabToSession.get(tab.id); + if (owner && owner !== session.id) throw createRpcError("Tab already owned"); session.tabIds.add(tab.id); session.origins.set(tab.id, origin); tabToSession.set(tab.id, session.id); @@ -407,6 +421,7 @@ class NativeTransport { const port = this.port; this.port = null; this.connected = false; + revokeAllSessions(); if (port) { try { port.disconnect(); @@ -470,8 +485,9 @@ class NativeTransport { this.connected = true; this.reconnectAttempt = 0; this.setStatus("connected").catch(() => undefined); - this.port.onMessage.addListener((message) => this.onMessage(message)); - this.port.onDisconnect.addListener(() => this.onDisconnect()); + const port = this.port; + port.onMessage.addListener((message) => { if (this.port === port) this.onMessage(message); }); + port.onDisconnect.addListener(() => { if (this.port === port) this.onDisconnect(); }); } catch (error) { this.connected = false; this.port = null; @@ -487,6 +503,7 @@ class NativeTransport { const message = chrome.runtime.lastError?.message || "Native host disconnected"; this.connected = false; this.port = null; + revokeAllSessions(); for (const { reject, timer } of this.pending.values()) { clearTimeout(timer); reject(new Error(message)); @@ -515,7 +532,8 @@ class NativeTransport { return; } if ("method" in rpcMessage) { - handleJsonRpcRequest(rpcMessage, (response) => this.post(response)).catch(() => undefined); + const port = this.port; + handleJsonRpcRequest(rpcMessage, (response) => { if (port && this.port === port) port.postMessage(response); }).catch(() => undefined); } } @@ -566,13 +584,156 @@ class NativeTransport { } } +function injectPage(tabId: number, origin: string, operation: "observe" | "act" | "capture-check" | "document-check" | "prepare-submit" | "submit", token = "", actionId = "", text?: string, documentId?: string, options: PageOptions = { selectors: [], controlsOnly: false }) { + return Effect.gen(function* () { + const chromeApi = yield* ChromeApi; + const owner = getSessionForTab(tabId); + if (!owner) throw createRpcError("Page owner revoked"); + const results = yield* chromeApi.call>[]>("scripting", "executeScript", { + target: documentId ? { tabId, documentIds: [documentId] } : { tabId, frameIds: [0] }, + world: "ISOLATED", func: pageControl, args: [operation, origin, token, actionId, text ?? null, options] + }); + const result = results[0]; + if (getSessionForTab(tabId) !== owner || !result?.documentId || result.frameId !== 0) throw createRpcError("Page changed or owner revoked"); + return result; + }); +} + +function scopedContext(tabId: number, origin: string, documentId?: string) { + return Effect.gen(function* () { + const chromeApi = yield* ChromeApi; + const key = `${PRIVATE_CAPTURE_KEY}:${tabId}`; + const previous = (yield* chromeApi.storageGet>(key))[key]; + if (typeof previous === "string") return yield* Effect.fail(createRpcError("private-quarantine")); + let selectors: string[] = [], quarantinedDocument: string | undefined; + if (previous != null) { + if (typeof previous !== "object" || !("documentId" in previous) || typeof previous.documentId !== "string" + || !("selectors" in previous) || !Array.isArray(previous.selectors) || previous.selectors.length > 256 + || !previous.selectors.every((s): s is string => typeof s === "string" && s.length > 0 && s.length <= 1024)) return yield* Effect.fail(createRpcError("invalid-private-selectors")); + selectors = previous.selectors; quarantinedDocument = previous.documentId; + } + const checked = yield* injectPage(tabId, origin, "document-check", "", "", undefined, documentId); + if (checked.result?.status !== "checked") return yield* Effect.fail(createRpcError("Page origin not ready or mismatch")); + if (quarantinedDocument === checked.documentId) return yield* Effect.fail(createRpcError("private-quarantine")); + return { documentId: checked.documentId, selectors }; + }); +} + +function assertCapture(tabId: number, origin: string) { + return Effect.gen(function* () { + const chromeApi = yield* ChromeApi; + const key = `${PRIVATE_CAPTURE_KEY}:${tabId}`; + const stored = yield* chromeApi.storageGet>(key); + const previous = stored[key]; + if (typeof previous === "string") throw createRpcError("private-quarantine"); + const checked = yield* injectPage(tabId, origin, "capture-check", "", "", JSON.stringify(previous?.selectors ?? [])); + if (checked.result?.status !== "checked") throw createRpcError("Page origin not ready or mismatch"); + if (previous?.documentId === checked.documentId) throw createRpcError("private-quarantine"); + if (checked.result.allowed !== true) { + switch (checked.result.reason) { + case "private-quarantine": case "populated-private-input": case "restored-private-selector": case "invalid-private-selectors": + throw createRpcError(checked.result.reason); + default: throw createRpcError("Capture privacy check failed"); + } + } + return checked.documentId; + }); +} + const api: Record = { + preparePrivateSubmit: (params: JsonRecord = {}) => Effect.gen(function* () { + const { tabId } = yield* Effect.try({ try: () => requireSessionTab(params), catch: toError }); + const origin = pageOrigins.get(tabId), snapshot = pageSnapshots.get(tabId); + pageSnapshots.delete(tabId); + if (!origin || !snapshot || params.snapshot !== snapshot.token || typeof params.actionId !== "string") throw createRpcError("Fresh observed submit action required"); + const context = yield* scopedContext(tabId, origin, snapshot.documentId); + const result = yield* injectPage(tabId, origin, "prepare-submit", snapshot.token, params.actionId, undefined, context.documentId, { selectors: context.selectors, controlsOnly: false }); + return { ...result.result, documentId: result.documentId }; + }), + + submitPrivate: (params: JsonRecord = {}) => Effect.gen(function* () { + const { tabId } = yield* Effect.try({ try: () => requireSessionTab(params), catch: toError }); + const origin = pageOrigins.get(tabId); + if (!origin || typeof params.submitToken !== "string" || typeof params.documentId !== "string" || pageRecordings.has(tabId)) return { status: "not-executed", reason: "invalid-or-recording" }; + const result = yield* Effect.either(injectPage(tabId, origin, "submit", params.submitToken, "", undefined, params.documentId)); + return Either.isRight(result) ? result.right.result : { status: "unknown", retry: false }; + }), + recordingState: (params: JsonRecord = {}) => Effect.gen(function* () { + const { tabId } = yield* Effect.try({ try: () => requireSessionTab(params), catch: toError }); + if (params.active === false) { pageRecordings.delete(tabId); return { recording: false }; } + const origin = pageOrigins.get(tabId); + if (!origin || params.active !== true || pageRecordings.has(tabId)) throw createRpcError("Recording unavailable or already active"); + yield* assertCapture(tabId, origin); + pageRecordings.add(tabId); + return { recording: true }; + }), + bindPage: (params: JsonRecord = {}) => Effect.gen(function* () { + const { tabId } = yield* Effect.try({ try: () => requireSessionTab(params), catch: toError }); + const origin = params.expectedOrigin; + if (typeof origin !== "string" || new URL(origin).origin !== origin || !/^https:\/\//.test(origin) && !(params.allowInsecureLoopback === true && /^http:\/\/(127\.0\.0\.1|localhost)(:\d+)?$/.test(origin))) throw createRpcError("Exact HTTPS origin required; loopback needs opt-in"); + if (pageOrigins.has(tabId) && pageOrigins.get(tabId) !== origin) throw createRpcError("Page origin binding is immutable"); + pageOrigins.set(tabId, origin); + return { bound: true, tabId, origin }; + }), + + navigatePage: (params: JsonRecord = {}) => Effect.gen(function* () { + const { tabId } = yield* Effect.try({ try: () => requireSessionTab(params), catch: toError }); + const origin = pageOrigins.get(tabId); + if (!origin || typeof params.url !== "string" || new URL(params.url).origin !== origin) throw createRpcError("Navigation outside bound origin refused"); + pageSnapshots.delete(tabId); + const result = yield* sendDebuggerCommand<{ errorText?: string }>(tabId, "Page.navigate", { url: params.url }); + if (result.errorText) throw createRpcError("Navigation failed; do not replay"); + return { status: "dispatched", retry: false }; + }), + + observePage: (params: JsonRecord = {}) => Effect.gen(function* () { + const { tabId } = yield* Effect.try({ try: () => requireSessionTab(params), catch: toError }); + const origin = pageOrigins.get(tabId); + if (!origin) throw createRpcError("Page has no origin binding"); + pageSnapshots.delete(tabId); + if (params.controlsOnly !== undefined && typeof params.controlsOnly !== "boolean") throw createRpcError("Invalid observation mode"); + const context = yield* scopedContext(tabId, origin); + const token = crypto.randomUUID(); + const result = yield* injectPage(tabId, origin, "observe", token, "", undefined, context.documentId, { selectors: context.selectors, controlsOnly: params.controlsOnly === true }); + if (result.result?.status !== "observed") { + switch (result.result?.reason) { + case "private-quarantine": case "populated-private-input": case "restored-private-selector": case "invalid-private-selectors": case "unsupported-shadow-root": + throw createRpcError(result.result.reason); + default: throw createRpcError("Observation unavailable"); + } + } + if (result.result?.status === "observed" && result.documentId) pageSnapshots.set(tabId, { token, documentId: result.documentId }); + return result.result; + }), + + actPage: (params: JsonRecord = {}) => Effect.gen(function* () { + const { tabId } = yield* Effect.try({ try: () => requireSessionTab(params), catch: toError }); + const origin = pageOrigins.get(tabId), snapshot = pageSnapshots.get(tabId); + pageSnapshots.delete(tabId); + if (!origin || !snapshot || params.snapshot !== snapshot.token || typeof params.actionId !== "string" || params.text !== undefined && typeof params.text !== "string") return { status: "not-executed", reason: "stale-or-invalid" }; + const context = yield* Effect.either(scopedContext(tabId, origin, snapshot.documentId)); + if (Either.isLeft(context)) return { status: "not-executed", reason: "privacy-or-document-changed", retry: false }; + const result = yield* Effect.either(injectPage(tabId, origin, "act", snapshot.token, params.actionId, params.text, context.right.documentId, { selectors: context.right.selectors, controlsOnly: false })); + return Either.isRight(result) ? result.right.result : { status: "unknown", retry: false }; + }), + + capturePage: (params: JsonRecord = {}) => Effect.gen(function* () { + const { tabId } = yield* Effect.try({ try: () => requireSessionTab(params), catch: toError }); + const origin = pageOrigins.get(tabId); + if (!origin) throw createRpcError("Page has no origin binding"); + const documentId = yield* assertCapture(tabId, origin); + const result = yield* sendDebuggerCommand(tabId, "Page.captureScreenshot", { format: "jpeg", quality: 80 }); + if ((yield* assertCapture(tabId, origin)) !== documentId) throw createRpcError("Document changed during capture; image discarded"); + return result; + }), ping: () => Effect.succeed("pong"), getInfo: () => Effect.gen(function* () { return { name: "Chrome", version: chrome.runtime.getManifest().version, + protocolVersion: 2, + pageProtocolVersion: 2, type: "extension", metadata: { extensionId: chrome.runtime.id, @@ -584,11 +745,8 @@ const api: Record = { getTabs: (params: JsonRecord = {}) => Effect.gen(function* () { const chromeApi = yield* ChromeApi; - const sessionId = params.session_id || params.sessionId; - const targetSessions = sessionId - ? Array.from(sessions.values()).filter((session) => session.id === sessionId) - : Array.from(sessions.values()); - const tabIds = targetSessions.flatMap((session) => Array.from(session.tabIds)); + const session = yield* Effect.try({ try: () => ensureSession(params), catch: toError }); + const tabIds = Array.from(session.tabIds); const tabs = yield* Effect.all(tabIds.map((id) => chromeApi.safeCall("tabs", "get", id))); return tabs.flatMap((tab) => { const info = tabInfo(tab); @@ -596,8 +754,9 @@ const api: Record = { }); }), - getUserTabs: () => Effect.gen(function* () { + getUserTabs: (params: JsonRecord = {}) => Effect.gen(function* () { const chromeApi = yield* ChromeApi; + yield* Effect.try({ try: () => ensureSession(params), catch: toError }); const tabs = yield* chromeApi.call("tabs", "query", {}); return tabs.flatMap((tab) => { if (tab.id == null || !isControllableUrl(tab.url) || tabToSession.has(tab.id)) return []; @@ -608,6 +767,7 @@ const api: Record = { getUserHistory: (params: JsonRecord = {}) => Effect.gen(function* () { const chromeApi = yield* ChromeApi; + yield* Effect.try({ try: () => ensureSession(params), catch: toError }); const maxResults = Math.max(1, Math.min(Number(params.limit) || 100, 1000)); const from = Number(params.from); const to = Number(params.to); @@ -624,13 +784,8 @@ const api: Record = { const chromeApi = yield* ChromeApi; const session = yield* Effect.try({ try: () => ensureSession(params), catch: toError }); const win = yield* findNormalWindow(); - let tab; - if (win?.id) { - tab = yield* chromeApi.call("tabs", "create", { windowId: win.id, url: "about:blank", active: true }); - } else { - const created = yield* chromeApi.call("windows", "create", { url: "about:blank", focused: false, type: "normal" }); - tab = created.tabs?.[0]; - } + if (!win?.id) return yield* Effect.fail(createRpcError("No normal window; background creation will not open a window")); + const tab = yield* chromeApi.call("tabs", "create", { windowId: win.id, url: "about:blank", active: false }); if (!tab?.id) return yield* Effect.fail(createRpcError("Failed to create tab")); yield* Effect.try({ try: () => registerSessionTab(session, tab, "agent"), catch: toError }); yield* ensureSessionGroup(session, tab.id); @@ -706,12 +861,14 @@ const api: Record = { attach: (params: JsonRecord = {}) => Effect.gen(function* () { const chromeApi = yield* ChromeApi; - const { tabId } = yield* Effect.try({ try: () => requireSessionTab(params), catch: toError }); + const { session, tabId } = yield* Effect.try({ try: () => requireSessionTab(params), catch: toError }); + const generation = tabGenerations.get(tabId) || 0; if (attachedTabs.has(tabId)) return { attached: true }; const attached = yield* Effect.either(chromeApi.call("debugger", "attach", { tabId }, DEBUGGER_VERSION)); - if (Either.isLeft(attached)) { - const message = attached.left.message; - if (!/Another debugger|already attached/i.test(message)) return yield* Effect.fail(attached.left); + if (Either.isLeft(attached)) return yield* Effect.fail(attached.left); + if (getSessionForTab(tabId) !== session || generation !== (tabGenerations.get(tabId) || 0)) { + yield* chromeApi.safeCall("debugger", "detach", { tabId }); + return yield* Effect.fail(createRpcError("Session revoked during attach")); } attachedTabs.add(tabId); return { attached: true }; @@ -725,13 +882,17 @@ const api: Record = { executeCdp: (params: JsonRecord = {}) => Effect.gen(function* () { const chromeApi = yield* ChromeApi; - const { tabId } = yield* Effect.try({ try: () => requireSessionTab(params), catch: toError }); + const { session, tabId } = yield* Effect.try({ try: () => requireSessionTab(params), catch: toError }); const method = String(params.method || ""); if (!method) return yield* Effect.fail(createRpcError("Missing CDP method")); + if (pageOrigins.has(tabId)) throw createRpcError("Raw CDP disabled for origin-bound pages; use typed page methods"); + const quarantine = yield* chromeApi.storageGet>(`${PRIVATE_CAPTURE_KEY}:${tabId}`); + if (quarantine[`${PRIVATE_CAPTURE_KEY}:${tabId}`]) throw createRpcError("Raw CDP disabled for a privately filled tab; bind and verify a new document first"); if (method === "Target.getTargets") { - const targetInfos = yield* chromeApi.call("debugger", "getTargets"); - return { targetInfos }; + const targets = yield* chromeApi.call("debugger", "getTargets"); + return { targetInfos: targets.filter(target => target.tabId != null && session.tabIds.has(target.tabId)) }; } + if (/^(Target|Browser)\./.test(method)) return yield* Effect.fail(createRpcError("Cross-target CDP methods are not supported")); if (!attachedTabs.has(tabId)) return yield* Effect.fail(createRpcError(`Tab ${tabId} is not attached`)); const commandParams: JsonRecord = typeof params.commandParams === "object" && params.commandParams != null ? params.commandParams as JsonRecord @@ -741,6 +902,78 @@ const api: Record = { return yield* sendDebuggerCommand(tabId, method, commandParams, timeoutMs); }), + observeDocument: (params: JsonRecord = {}) => Effect.gen(function* () { + const chromeApi = yield* ChromeApi; + const { session, tabId } = yield* Effect.try({ try: () => requireSessionTab(params), catch: toError }); + const generation = tabGenerations.get(tabId) || 0; + privateObservations.delete(tabId); + const origin = params.expectedOrigin; + const expectedUrl = params.expectedUrl; + const selectors = params.selectors; + if (pageOrigins.has(tabId) && pageOrigins.get(tabId) !== origin) throw createRpcError("Private fill origin differs from bound page origin"); + if (typeof origin !== "string" || !/^https?:\/\//.test(origin) || new URL(origin).origin !== origin + || !Array.isArray(selectors) || !selectors.length || selectors.length > 16 + || !selectors.every((value): value is string => typeof value === "string" && value.length > 0 && value.length <= 1024)) { + return yield* Effect.fail(createRpcError("Invalid private observation request")); + } + const parsedOrigin = new URL(origin); + if (expectedUrl !== undefined && (typeof expectedUrl !== "string" || expectedUrl.length > 16384)) { + return yield* Effect.fail(createRpcError("Invalid private observation URL")); + } + if (typeof expectedUrl === "string") { + const parsedUrl = yield* Effect.try({ try: () => new URL(expectedUrl), catch: () => createRpcError("Invalid private observation URL") }); + if (parsedUrl.href !== expectedUrl || parsedUrl.origin !== origin) { + return yield* Effect.fail(createRpcError("Private observation URL differs from expected origin")); + } + } + const allowInsecureLoopback = params.allowInsecureLoopback === true; + if (parsedOrigin.protocol !== "https:" && !(allowInsecureLoopback && ["127.0.0.1", "localhost", "[::1]"].includes(parsedOrigin.hostname))) { + return yield* Effect.fail(createRpcError("Private input requires HTTPS; loopback HTTP requires explicit opt-in")); + } + const token = crypto.randomUUID(); + const results = yield* chromeApi.call[]>("scripting", "executeScript", { + target: { tabId, frameIds: [0] }, world: "ISOLATED", func: observePrivateFields, args: [origin, selectors, token, allowInsecureLoopback, expectedUrl] + }); + const observed = results[0]; + if (!observed?.documentId || observed.result?.status !== "observed" || observed.result.origin !== origin + || typeof observed.result.url !== "string" || (expectedUrl !== undefined && observed.result.url !== expectedUrl) + || new URL(observed.result.url).origin !== origin + || getSessionForTab(tabId) !== session || generation !== (tabGenerations.get(tabId) || 0)) { + return yield* Effect.fail(createRpcError("Private observation refused")); + } + const url = observed.result.url; + privateObservations.set(tabId, { token, origin, url, documentId: observed.documentId, expires: Date.now() + 30000, selectors }); + return { token, origin, url, documentId: observed.documentId, expiresInMs: 30000 }; + }), + + privateFill: (params: JsonRecord = {}) => Effect.gen(function* () { + const chromeApi = yield* ChromeApi; + const { tabId, session } = yield* Effect.try({ try: () => requireSessionTab(params), catch: toError }); + if (pageRecordings.has(tabId)) return { status: "refused", retry: false }; + if (!pageOrigins.has(tabId)) return { status: "refused", retry: false }; + const observed = privateObservations.get(tabId); + privateObservations.delete(tabId); + const values = params.values; + if (!observed || pageOrigins.get(tabId) !== observed.origin || params.token !== observed.token || params.documentId !== observed.documentId + || params.expectedOrigin !== observed.origin || observed.expires < Date.now() + || !Array.isArray(values) || !values.length || values.length > 16 + || !values.every((value): value is string => typeof value === "string" && value.length <= 16384)) { + return { status: "refused", retry: false }; + } + const key = `${PRIVATE_CAPTURE_KEY}:${tabId}`; + const previous = (yield* chromeApi.storageGet>(key))[key]; + if (typeof previous === "string") return { status: "refused", retry: false }; + const selectors = [...new Set([...(previous?.selectors ?? []), ...observed.selectors])]; + if (selectors.length > 256) return { status: "refused", retry: false }; + yield* chromeApi.storageSet({ [key]: { documentId: observed.documentId, selectors } }); + if (getSessionForTab(tabId) !== session) return { status: "refused", retry: false }; + const outcome = yield* Effect.either(chromeApi.call[]>("scripting", "executeScript", { + target: { tabId, documentIds: [observed.documentId] }, world: "ISOLATED", func: fillPrivateFields, + args: [observed.origin, observed.token, values] + })); + return { status: Either.isRight(outcome) && outcome.right[0]?.result?.status === "filled" ? "filled" : "unknown", retry: false }; + }), + moveMouse: (params: JsonRecord = {}) => Effect.gen(function* () { const { session, tabId } = yield* Effect.try({ try: () => requireSessionTab(params), catch: toError }); const x = Number(params.x); @@ -799,28 +1032,47 @@ const api: Record = { async function handleJsonRpcRequest(message: JsonRpcRequest, respond: (response: unknown) => void) { const { id, method, params } = message; + if (method === "internal.releaseClient") { + if (typeof params === "object" && params !== null && "session_id" in params && typeof params.session_id === "string") revokeSession(params.session_id); + return; + } if (!method || typeof method !== "string") { if (id != null) respond(jsonRpcError(id, createRpcError("Invalid JSON-RPC method", -32600))); return; } - const fn = api[method] || api.executeUnhandledCommand; + const fn = Object.hasOwn(api, method) ? api[method] : api.executeUnhandledCommand; try { - const result = await runChromeEffect(Effect.gen(function* () { + const run = () => runChromeEffect(Effect.gen(function* () { const rpcParams = typeof params === "object" && params != null ? params as JsonRecord : {}; return yield* fn(rpcParams); })); + const rpcParams = typeof params === "object" && params != null ? params as JsonRecord : {}; + const targetParams = typeof rpcParams.target === "object" && rpcParams.target != null ? rpcParams.target as JsonRecord : {}; + const tabId = Number(rpcParams.tabId ?? targetParams.tabId); + const serialized = ["preparePrivateSubmit", "submitPrivate", "recordingState", "bindPage", "navigatePage", "observePage", "actPage", "capturePage", "observeDocument", "privateFill", "executeCdp"].includes(method) && Number.isInteger(tabId); + let result: unknown; + if (serialized) { + const previous = pageOperations.get(tabId) || Promise.resolve(); + const generation = tabGenerations.get(tabId) || 0; + const next = previous.catch(() => undefined).then(() => { + if (generation !== (tabGenerations.get(tabId) || 0)) throw createRpcError("Stale queued page operation; not dispatched"); + return run(); + }); + pageOperations.set(tabId, next); + try { result = await next; } finally { if (pageOperations.get(tabId) === next) pageOperations.delete(tabId); } + } else result = await run(); if (id != null) respond({ jsonrpc: "2.0", id, result }); } catch (error) { - if (id != null) respond(jsonRpcError(id, error)); + if (id != null) respond(jsonRpcError(id, method === "privateFill" ? createRpcError("Private input refused or outcome unknown; do not replay") : error)); } } function enqueueCdp(tabId: number, task: () => Promise) { const previous = commandQueues.get(tabId) || Promise.resolve(); const next = previous.catch(() => undefined).then(task); - commandQueues.set(tabId, next.finally(() => { - if (commandQueues.get(tabId) === next) commandQueues.delete(tabId); - })); + const settled = next.then(() => undefined, () => undefined); + commandQueues.set(tabId, settled); + void settled.then(() => { if (commandQueues.get(tabId) === settled) commandQueues.delete(tabId); }); return next; } @@ -832,8 +1084,14 @@ function sendDebuggerCommand( ) { return Effect.gen(function* () { const chromeApi = yield* ChromeApi; + const generation = tabGenerations.get(tabId) || 0; + const owner = tabToSession.get(tabId); return yield* Effect.tryPromise({ - try: async () => await enqueueCdp(tabId, () => withTimeout( + try: async () => await enqueueCdp(tabId, () => { + if (!attachedTabs.has(tabId) || !owner || tabToSession.get(tabId) !== owner || (tabGenerations.get(tabId) || 0) !== generation) { + throw createRpcError("Stale command; not replayed"); + } + return withTimeout( Effect.runPromise(chromeApi.call("debugger", "sendCommand", { tabId }, method, commandParams)), timeoutMs, `CDP command ${method}` @@ -841,7 +1099,7 @@ function sendDebuggerCommand( const message = error instanceof Error ? error.message : String(error); if (/timed out/i.test(message)) await runChromeEffect(detachTab(tabId)); throw error; - })) as T, + }); }) as T, catch: toError }); }); @@ -916,7 +1174,7 @@ function detachTab(tabId: number) { return Effect.gen(function* () { const chromeApi = yield* ChromeApi; attachedTabs.delete(tabId); - commandQueues.delete(tabId); + invalidateTab(tabId); yield* chromeApi.safeCall("debugger", "detach", { tabId }); }); } @@ -967,8 +1225,7 @@ function waitForCursorArrival(tabId: number, sessionId: string, turnId: string, function stopActiveSessions(reason: string) { return Effect.gen(function* () { - for (const tabId of Array.from(attachedTabs)) yield* detachTab(tabId); - for (const session of sessions.values()) session.active = false; + revokeAllSessions(); nativeTransport?.notify("onControlStopped", { reason }); }); } @@ -1032,12 +1289,14 @@ chrome.runtime.onMessage.addListener((message: unknown, sender, sendResponse) => }); chrome.debugger.onEvent.addListener((source, method, params) => { - nativeTransport?.notify("onCDPEvent", { source, method, params: params || {} }); + const session = source.tabId == null ? null : getSessionForTab(source.tabId); + if (session) nativeTransport?.notify("onCDPEvent", { session_id: session.id, source, method, params: params || {} }); }); chrome.debugger.onDetach.addListener((source, reason) => { - if (source.tabId != null && Number.isInteger(source.tabId)) attachedTabs.delete(source.tabId); - nativeTransport?.notify("onCDPDetach", { source, reason }); + const session = source.tabId == null ? null : getSessionForTab(source.tabId); + if (source.tabId != null && Number.isInteger(source.tabId)) { attachedTabs.delete(source.tabId); invalidateTab(source.tabId); } + if (session) nativeTransport?.notify("onCDPDetach", { session_id: session.id, source, reason }); }); chrome.downloads.onCreated.addListener((item) => { @@ -1066,6 +1325,7 @@ chrome.downloads.onChanged.addListener((delta) => { }); chrome.tabs.onRemoved.addListener((tabId) => { + invalidateTab(tabId); const session = getSessionForTab(tabId); if (session) { session.tabIds.delete(tabId); @@ -1083,9 +1343,49 @@ chrome.tabs.onReplaced.addListener((addedTabId, removedTabId) => { const origin = session.origins.get(removedTabId) || "user"; session.tabIds.delete(removedTabId); session.origins.delete(removedTabId); - registerSessionTab(session, { id: addedTabId }, origin); + tabToSession.delete(removedTabId); + tabOrigins.delete(removedTabId); + attachedTabs.delete(removedTabId); + invalidateTab(removedTabId); + try { registerSessionTab(session, { id: addedTabId }, origin); } + catch { revokeSession(session.id); } +}); + +chrome.tabs.onUpdated.addListener((tabId, changeInfo) => { + if (changeInfo.status === "loading" || changeInfo.url) invalidateTab(tabId); }); +function invalidateTab(tabId: number) { + tabGenerations.set(tabId, (tabGenerations.get(tabId) || 0) + 1); + privateObservations.delete(tabId); + pageSnapshots.delete(tabId); +} + +function revokeSession(sessionId: string) { + revokedSessions.add(sessionId); + if (revokedSessions.size > 4096) { + const oldest = revokedSessions.values().next().value; + if (oldest !== undefined) revokedSessions.delete(oldest); + } + const session = sessions.get(sessionId); + if (!session) return; + sessions.delete(sessionId); + for (const tabId of session.tabIds) { + tabToSession.delete(tabId); + pageOrigins.delete(tabId); + pageRecordings.delete(tabId); + tabOrigins.delete(tabId); + cursorStates.delete(tabId); + invalidateTab(tabId); + void runChromeEffect(detachTab(tabId)).catch(() => undefined); + } + void runChromeEffect(persistGroupState()).catch(() => undefined); +} + +function revokeAllSessions() { + for (const id of sessions.keys()) revokeSession(id); +} + chrome.alarms.onAlarm.addListener((alarm) => { if (alarm.name === RECONNECT_ALARM && !nativeTransport?.connected && !nativeTransport?.paused) nativeTransport.connect(); if (alarm.name === HEARTBEAT_ALARM && nativeTransport?.connected) { diff --git a/src/extension/manifest.json b/src/extension/manifest.json index 86736f3..a022aec 100644 --- a/src/extension/manifest.json +++ b/src/extension/manifest.json @@ -1,8 +1,9 @@ { "manifest_version": 3, + "minimum_chrome_version": "106", "name": "Chrome Control", "description": "Control Chrome with the Chrome Control local agent bridge.", - "version": "0.1.3", + "version": "0.2.0", "background": { "service_worker": "background.js" }, diff --git a/src/extension/page-control.ts b/src/extension/page-control.ts new file mode 100644 index 0000000..40022fb --- /dev/null +++ b/src/extension/page-control.ts @@ -0,0 +1,181 @@ +import type { PageAction } from "../shared/page-protocol"; +export type { PageAction } from "../shared/page-protocol"; +type Target = { node: Element; root: Node; form: HTMLFormElement | null; ancestry: Node[]; marker: string; action: PageAction }; +type Snapshot = { token: string; origin: string; document: Document; targets: Target[]; expires: number }; +type World = typeof globalThis & { __opzeroSnapshot?: Snapshot; __opzeroPrivateNodes?: WeakSet; __opzeroPrivateDocument?: boolean; __opzeroPrivateForms?: Set; __opzeroSubmit?: { token: string; target: Target; node: HTMLButtonElement | HTMLInputElement; form: HTMLFormElement; expires: number } }; +export type PageOptions = { selectors: string[]; controlsOnly: boolean }; + +// executeScript serializes this function without module bindings. +export function pageControl(operation: "observe" | "act" | "capture-check" | "document-check" | "prepare-submit" | "submit", expectedOrigin: string, token: string, actionId = "", text?: string | null, options: PageOptions = { selectors: [], controlsOnly: false }) { + const world = globalThis as World; + const visible = (e: Element) => e.checkVisibility({ checkOpacity: true, checkVisibilityCSS: true }); + const privateInput = (e: Element) => world.__opzeroPrivateNodes?.has(e) || (e instanceof HTMLInputElement && (e.type === "password" || e.autocomplete === "one-time-code")); + const populated = () => [...document.querySelectorAll("input,textarea")].some(e => (e instanceof HTMLInputElement || e instanceof HTMLTextAreaElement) && e.value && privateInput(e)); + const snapshot = world.__opzeroSnapshot; + if (operation !== "capture-check" && operation !== "document-check") delete world.__opzeroSnapshot; + const refused = (reason: string) => ({ status: operation === "observe" ? "refused" : "not-executed", reason, retry: false }); + if (location.origin !== expectedOrigin || window !== window.top) return refused("origin"); + if (operation === "document-check") return { status: "checked" }; + if (operation === "capture-check") { + if (world.__opzeroPrivateDocument) return { status: "checked", allowed: false, reason: "private-quarantine" }; + try { + const selectors: unknown = JSON.parse(text || "[]"); + if (!Array.isArray(selectors) || !selectors.every(selector => typeof selector === "string")) return { status: "checked", allowed: false, reason: "invalid-private-selectors" }; + const restored = selectors.some(selector => [...document.querySelectorAll(selector)].some(e => (e instanceof HTMLInputElement || e instanceof HTMLTextAreaElement) && e.value)); + if (populated()) return { status: "checked", allowed: false, reason: "populated-private-input" }; + if (restored) return { status: "checked", allowed: false, reason: "restored-private-selector" }; + return { status: "checked", allowed: true }; + } catch { + return { status: "checked", allowed: false, reason: "invalid-private-selectors" }; + } + } + + const prepared = operation === "submit" ? world.__opzeroSubmit : undefined; + if (operation === "submit" || operation === "prepare-submit") delete world.__opzeroSubmit; + if (operation !== "submit" && world.__opzeroPrivateDocument) return refused("private-quarantine"); + if (typeof chrome === "undefined" || typeof chrome.dom?.openOrClosedShadowRoot !== "function") return refused("unsupported-shadow-root"); + const elements = new Set(); + const roots: (Document | ShadowRoot)[] = [document]; + const opaqueSurfaces: { id: string; kind: "iframe" | "frame" | "object" | "embed" | "closed-shadow-root" }[] = []; + let opaqueCount = 0; + const opaque = (kind: typeof opaqueSurfaces[number]["kind"]) => { + if (opaqueCount < 100) opaqueSurfaces.push({ id: `opaque-${opaqueCount}`, kind }); + opaqueCount++; + }; + const walk = (node: Node) => { + if (node instanceof Element) { + const tag = node.localName; + if (tag === "iframe" || tag === "frame" || tag === "object" || tag === "embed") { opaque(tag); return; } + const shadow = node instanceof HTMLElement ? chrome.dom.openOrClosedShadowRoot(node) : null; + if (shadow?.mode === "closed") { opaque("closed-shadow-root"); return; } + elements.add(node); + if (shadow) { roots.push(shadow); walk(shadow); } + } + for (const child of node.childNodes) walk(child); + }; + try { walk(document); } catch { return refused("unsupported-shadow-root"); } + const inputs = roots.flatMap(root => [...root.querySelectorAll("input,textarea")]) + .filter((e): e is HTMLInputElement | HTMLTextAreaElement => e instanceof HTMLInputElement || e instanceof HTMLTextAreaElement); + const privateNodes = new Set(inputs.filter(privateInput)); + try { + if (!Array.isArray(options.selectors) || !options.selectors.every(s => typeof s === "string")) return refused("invalid-private-selectors"); + for (const selector of options.selectors) for (const root of roots) for (const e of root.querySelectorAll(selector)) { + if (e instanceof HTMLInputElement || e instanceof HTMLTextAreaElement) privateNodes.add(e); + } + } catch { return refused("invalid-private-selectors"); } + const privacy = inputs.some(e => e.value && privateInput(e)) ? "populated-private-input" + : inputs.some(e => e.value && privateNodes.has(e)) ? "restored-private-selector" : null; + if (operation !== "submit" && privacy) return refused(privacy); + const privateLabels = new Set(); + for (const e of inputs) if (privateNodes.has(e)) { + for (const label of e.labels ?? []) privateLabels.add(label); + for (const id of (e.getAttribute("aria-labelledby") || "").split(/\s+/)) { + const root = e.getRootNode(); + const label = root instanceof ShadowRoot ? root.getElementById(id) : document.getElementById(id); + if (label) privateLabels.add(label); + } + } + const ancestry = (node: Node) => { + const chain: Node[] = []; + for (let current: Node | null = node; current && !chain.includes(current);) { + chain.push(current); + current = (current instanceof Element || current instanceof Text) && current.assignedSlot ? current.assignedSlot + : current instanceof ShadowRoot ? current.host : current.parentNode; + } + return chain; + }; + const exposed = (e: Element) => elements.has(e) && ancestry(e).every(n => { + if (!(n instanceof Element)) return true; + if (!elements.has(n) || n.hasAttribute("inert") || n.hasAttribute("hidden") || n.getAttribute("aria-hidden") === "true") return false; + const style = getComputedStyle(n); + if (style.display === "none" || style.visibility === "hidden" || style.visibility === "collapse" || style.opacity === "0") return false; + if (n.parentElement?.shadowRoot && !n.assignedSlot) return false; + if (n.parentElement instanceof HTMLSlotElement && n.parentElement.assignedNodes().length) return false; + return true; + }); + const modals = [...elements].filter(e => e.matches(":modal") || e.getAttribute("aria-modal") === "true" && exposed(e)); + const available = (e: Element) => exposed(e) && visible(e) && modals.every(m => ancestry(e).includes(m)); + const safeText = (node: Node, requireVisible = true): string => { + if (node instanceof Element && (!elements.has(node) || ancestry(node).some(n => n instanceof Element && (privateNodes.has(n) || privateLabels.has(n))) + || ["script", "style", "template", "noscript", "input", "textarea", "select"].includes(node.localName) + || requireVisible && !exposed(node))) return ""; + if (node.nodeType === Node.TEXT_NODE) return node.nodeValue ?? ""; + const children = node instanceof HTMLSlotElement && node.assignedNodes().length ? node.assignedNodes() + : node instanceof Element && node.shadowRoot ? [node.shadowRoot] : [...node.childNodes]; + const parts = children.map(child => safeText(child, requireVisible)); + return parts.join(" "); + }; + const normalize = (value: string) => value.replace(/\s+/g, " ").trim(); + const label = (e: Element) => { + const labelled = (e.getAttribute("aria-labelledby") || "").split(/\s+/).filter(Boolean) + .map(id => e.getRootNode() instanceof ShadowRoot ? (e.getRootNode() as ShadowRoot).getElementById(id) : document.getElementById(id)) + .filter((n): n is HTMLElement => !!n && elements.has(n)); + return normalize(labelled.length ? labelled.map(n => safeText(n)).join(" ") : e.getAttribute("aria-label") + || (e instanceof HTMLInputElement || e instanceof HTMLTextAreaElement ? [...(e.labels ?? [])].map(n => safeText(n)).join(" ") || e.getAttribute("placeholder") || "" : safeText(e))); + }; + const kind = (e: Element): "fill" | "click" | null => { + if (ancestry(e).some(n => n instanceof Element && (privateNodes.has(n) || privateLabels.has(n)))) return null; + if (e instanceof HTMLTextAreaElement) return "fill"; + if (e instanceof HTMLInputElement) return ["text", "email", "search", "tel", "url", "number"].includes(e.type) ? "fill" : ["button", "submit"].includes(e.type) ? "click" : null; + return e.matches("button,a[href],[role=button]") ? "click" : null; + }; + const disabled = (e: Element) => e.matches(":disabled") || ancestry(e).some(n => n instanceof Element && n.getAttribute("aria-disabled") === "true") || (e instanceof HTMLInputElement || e instanceof HTMLTextAreaElement) && e.readOnly; + const form = (e: Element) => e instanceof HTMLButtonElement || e instanceof HTMLInputElement || e instanceof HTMLTextAreaElement ? e.form : null; + const semantics = (e: Element) => JSON.stringify([location.href, kind(e), label(e), + ["role", "type", "href", "target", "disabled", "aria-disabled", "aria-checked", "aria-expanded", "aria-pressed", "readonly", "min", "max", "step", "pattern", "maxlength"].map(a => e.getAttribute(a)), + disabled(e), e instanceof HTMLInputElement || e instanceof HTMLTextAreaElement ? e.value : null, + (e instanceof HTMLButtonElement || e instanceof HTMLInputElement) && e.form ? [e.formAction, e.form.action, e.formTarget, e.form.target, e.form.method] : null, + ancestry(e).filter((n): n is Element => n instanceof Element).map(n => [n.getAttribute("inert"), n.getAttribute("aria-hidden"), n.getAttribute("aria-disabled"), n.getAttribute("role")])]); + const target = (node: Element, id: string): Target => ({ node, root: node.getRootNode(), form: form(node), ancestry: ancestry(node), marker: semantics(node), + action: { id, kind: kind(node)!, label: label(node).slice(0, 160), role: (node.getAttribute("role") || node.localName).slice(0, 80), disabled: disabled(node) } }); + const fresh = (t: Target) => t.node.isConnected && t.node.ownerDocument === document && elements.has(t.node) && !privateNodes.has(t.node) + && t.root === t.node.getRootNode() && t.form === form(t.node) && available(t.node) && !disabled(t.node) && t.marker === semantics(t.node) + && t.ancestry.length === ancestry(t.node).length && t.ancestry.every((n, i) => n === ancestry(t.node)[i]); + const sameOriginSubmit = (node: HTMLButtonElement | HTMLInputElement) => node.type === "submit" && node.form + && new URL(node.formAction || node.form.action).origin === expectedOrigin && (!(node.formTarget || node.form.target) || (node.formTarget || node.form.target) === "_self"); + if (operation === "submit") { + if (!prepared || prepared.token !== token || prepared.expires < Date.now() || !world.__opzeroPrivateDocument || !world.__opzeroPrivateForms?.has(prepared.form) + || prepared.node.form !== prepared.form || !fresh(prepared.target) || !sameOriginSubmit(prepared.node)) return refused("private-submit-refused"); + try { prepared.node.click(); return { status: "executed", retry: false }; } catch { return { status: "unknown", retry: false }; } + } + if (operation === "observe") { + const nodes = [...elements].filter(e => kind(e) && available(e)); + const targets = nodes.slice(0, 100).map((node, i) => target(node, String(i))); + const body = options.controlsOnly ? "" : normalize(document.body ? safeText(document.body) : ""); + const titleNode = document.querySelector("head > title"); + const title = normalize(titleNode ? safeText(titleNode, false) : ""); + world.__opzeroSnapshot = { token, origin: expectedOrigin, document, targets, expires: Date.now() + 30000 }; + return { status: "observed", pageProtocolVersion: 2, url: location.href, title: title.slice(0, 200), text: body.slice(0, 12000), + actions: targets.map(t => t.action), snapshot: token, mode: options.controlsOnly ? "controls-only" : "full", partial: opaqueCount > 0, opaqueSurfaces, + truncation: { text: body.length > 12000, actions: nodes.length > 100, opaqueSurfaces: opaqueCount > 100, labels: targets.some(t => label(t.node).length > 160 || (t.node.getAttribute("role") || "").length > 80), title: title.length > 200 } }; + } + if (!snapshot || snapshot.token !== token || snapshot.origin !== expectedOrigin || snapshot.document !== document || snapshot.expires < Date.now()) return refused("stale"); + const t = snapshot.targets.find(t => t.action.id === actionId); + if (!t || !fresh(t)) return refused("stale"); + const node = t.node, action = t.action; + if (operation === "prepare-submit") { + if (!(node instanceof HTMLButtonElement || node instanceof HTMLInputElement) || !sameOriginSubmit(node) || !node.form) return refused("not-same-origin-submit"); + const submitToken = crypto.randomUUID(); world.__opzeroSubmit = { token: submitToken, target: t, node, form: node.form, expires: Date.now() + 30000 }; + return { status: "prepared", submitToken }; + } + if (action.kind === "fill") { + if (typeof text !== "string" || text.length > 2000 || !(node instanceof HTMLInputElement || node instanceof HTMLTextAreaElement)) return refused("invalid-fill"); + if (node instanceof HTMLInputElement && node.type === "number" && text !== "" && (!Number.isFinite(Number(text)) || !/^-?(?:\d+(?:\.\d+)?|\.\d+)(?:e[+-]?\d+)?$/i.test(text))) return refused("invalid-fill"); + const prototype = node instanceof HTMLInputElement ? HTMLInputElement.prototype : HTMLTextAreaElement.prototype; + const setter = Object.getOwnPropertyDescriptor(prototype, "value")?.set; + if (!setter) return refused("unsupported"); + try { + setter.call(node, text); + if (node.value !== text) return { status: "unknown", retry: false }; + node.dispatchEvent(new Event("input", { bubbles: true, composed: true })); + node.dispatchEvent(new Event("change", { bubbles: true })); + } catch { return { status: "unknown", retry: false }; } + } else { + if (text != null) return refused("unexpected-text"); + if (node instanceof HTMLAnchorElement && (new URL(node.href).origin !== expectedOrigin || node.target && node.target !== "_self")) return refused("cross-origin-or-new-tab"); + if ((node instanceof HTMLButtonElement || node instanceof HTMLInputElement) && node.type === "submit" && node.form && !sameOriginSubmit(node)) return refused("cross-origin-or-new-tab-submit"); + if (!(node instanceof HTMLElement)) return refused("unsupported"); + try { node.click(); } catch { return { status: "unknown", retry: false }; } + } + return { status: "executed", retry: false }; +} diff --git a/src/extension/private-input.ts b/src/extension/private-input.ts new file mode 100644 index 0000000..1336391 --- /dev/null +++ b/src/extension/private-input.ts @@ -0,0 +1,56 @@ +type Observation = { token: string; origin: string; url: string; document: Document; nodes: (HTMLInputElement | HTMLTextAreaElement)[]; selectors: string[] }; +type PrivateWorld = typeof globalThis & { __opzeroPrivateInput?: Observation; __opzeroPrivateNodes?: WeakSet; __opzeroPrivateDocument?: boolean; __opzeroPrivateForms?: Set }; + +export function observePrivateFields(expectedOrigin: string, selectors: string[], token: string, allowInsecureLoopback = false, expectedUrl?: string) { + const world = globalThis as PrivateWorld; + delete world.__opzeroPrivateInput; + const secure = location.protocol === "https:" || (allowInsecureLoopback && location.protocol === "http:" && ["127.0.0.1", "localhost", "[::1]"].includes(location.hostname)); + const url = location.href; + if (location.origin !== expectedOrigin || (expectedUrl !== undefined && url !== expectedUrl) || !secure || window !== window.top) return { status: "refused" }; + try { + const nodes = selectors.map(selector => { + const matches = document.querySelectorAll(selector); + const node = matches[0]; + if (matches.length !== 1 || !(node instanceof HTMLInputElement || node instanceof HTMLTextAreaElement) + || node.disabled || node.readOnly || node.closest("[inert]") || !node.checkVisibility({ checkOpacity: true, checkVisibilityCSS: true, opacityProperty: true, visibilityProperty: true }) + || (node instanceof HTMLInputElement && !["text", "email", "password", "tel", "search", "url"].includes(node.type))) throw new Error(); + return node; + }); + if (new Set(nodes).size !== nodes.length) return { status: "refused" }; + world.__opzeroPrivateInput = { token, origin: location.origin, url, document, nodes, selectors }; + return { status: "observed", origin: location.origin, url }; + } catch { return { status: "refused" }; } +} + +export function fillPrivateFields(expectedOrigin: string, token: string, values: string[]) { + const world = globalThis as PrivateWorld; + const observed = world.__opzeroPrivateInput; + delete world.__opzeroPrivateInput; + const fresh = () => observed && observed.token === token && observed.origin === expectedOrigin + && location.origin === expectedOrigin && location.href === observed.url && observed.document === document && window === window.top + && observed.nodes.every((node, i) => node.isConnected && node.ownerDocument === document + && document.querySelectorAll(observed.selectors[i]).length === 1 && document.querySelector(observed.selectors[i]) === node + && !node.disabled && !node.readOnly && !node.closest("[inert]") && node.checkVisibility({ checkOpacity: true, checkVisibilityCSS: true, opacityProperty: true, visibilityProperty: true }) + && (!(node instanceof HTMLInputElement) || ["text", "email", "password", "tel", "search", "url"].includes(node.type))); + try { + if (!fresh() || !observed || observed.nodes.length !== values.length) return { status: "refused" }; + world.__opzeroPrivateDocument = true; + world.__opzeroPrivateNodes ??= new WeakSet(); + world.__opzeroPrivateForms ??= new Set(); + for (const node of observed.nodes) { + world.__opzeroPrivateNodes.add(node); + if (node.form) world.__opzeroPrivateForms.add(node.form); + } + for (let i = 0; i < values.length; i++) { + if (!fresh()) return { status: "unknown" }; + const node = observed.nodes[i]; + const prototype = node instanceof HTMLInputElement ? HTMLInputElement.prototype : HTMLTextAreaElement.prototype; + const setter = Object.getOwnPropertyDescriptor(prototype, "value")?.set; + if (!setter) return { status: "unknown" }; + setter.call(node, values[i]); + node.dispatchEvent(new Event("input", { bubbles: true })); + node.dispatchEvent(new Event("change", { bubbles: true })); + } + return { status: fresh() ? "filled" : "unknown" }; + } catch { return { status: "unknown" }; } +} diff --git a/src/native-host/client.ts b/src/native-host/client.ts index 3e5708a..bb22f03 100755 --- a/src/native-host/client.ts +++ b/src/native-host/client.ts @@ -1,79 +1,113 @@ #!/usr/bin/env node +import fs from "node:fs"; import net from "node:net"; import os from "node:os"; import path from "node:path"; import process from "node:process"; -import { Context, Effect, Layer } from "effect"; -import { NodeRuntime } from "@effect/platform-node"; +import { parseJsonRpcMessage, isJsonRpcRequest } from "../shared/rpc"; -type ClientConfig = { - method: string; - params: Record; - useTcp: boolean; - port: number; - socketPath: string; -}; - -type ClientIoService = { - readConfig: () => Effect.Effect; - request: (config: ClientConfig) => Effect.Effect; - writeStdout: (text: string) => Effect.Effect; - writeStderr: (text: string) => Effect.Effect; -}; - -class ClientIo extends Context.Tag("opzero/NativeHostClientIo")() {} - -function toError(error: unknown) { - return error instanceof Error ? error : new Error(String(error || "Unknown error")); +const args = process.argv.slice(2); +if (args[0] === "--") args.shift(); +const streaming = args[0] === "--stdio"; +const method = args[0] || "ping"; +if (args.length > 1 || (!streaming && !["ping", "getInfo", "host.ping", "host.info"].includes(method))) { + process.stderr.write("Payloads are accepted only through --stdio JSONL; never pass private values in argv\n"); + process.exit(1); } +const useTcp = process.platform === "win32" || process.env.OPZERO_CHROME_HOST_TRANSPORT === "tcp"; +const socket = useTcp ? net.connect(Number(process.env.OPZERO_CHROME_HOST_PORT || 17365), "127.0.0.1") + : net.connect(process.env.OPZERO_CHROME_HOST_SOCKET || path.join(os.homedir(), ".opzero-chrome", "default.sock")); +const pending = new Map(); +let ready = false; +let inputEnded = false; +let input = ""; +let output = ""; +let stopped = false; +const maxBytes = 1024 * 1024; +const maxResponseBytes = 64 * maxBytes; -function parseArgs(argv: string[]) { - const args = argv.slice(2); - if (args[0] === "--") args.shift(); - const method = args[0] || "ping"; - const params = args[1] ? JSON.parse(args[1]) : {}; - return { method, params }; +function fail() { + if (stopped) return; + stopped = true; + process.stderr.write("Private client stopped; outcome may be unknown; do not replay\n"); + socket.destroy(); + process.exitCode = 1; + process.stdin.destroy(); } - -function requestNativeHost(config: ClientConfig) { - return new Promise((resolve, reject) => { - const socket = config.useTcp ? net.connect(config.port, "127.0.0.1") : net.connect(config.socketPath); - socket.setEncoding("utf8"); - socket.on("connect", () => { - socket.write(`${JSON.stringify({ jsonrpc: "2.0", id: 1, method: config.method, params: config.params })}\n`); - }); - socket.on("data", (chunk) => { - resolve(String(chunk)); - socket.end(); - }); - socket.on("error", reject); - }); +function finish() { + socket.setTimeout(pending.size ? 35000 : 0); + if (inputEnded && pending.size === 0) socket.end(); } - -const ClientIoLive = Layer.succeed(ClientIo, { - readConfig: () => Effect.try({ - try: () => { - const { method, params } = parseArgs(process.argv); - return { - method, - params, - useTcp: process.platform === "win32" || process.env.OPZERO_CHROME_HOST_TRANSPORT === "tcp", - port: Number(process.env.OPZERO_CHROME_HOST_PORT || 17365), - socketPath: process.env.OPZERO_CHROME_HOST_SOCKET || path.join(os.tmpdir(), "opzero-chrome-native-host.sock") - }; - }, - catch: toError - }), - request: (config: ClientConfig) => Effect.tryPromise({ try: () => requestNativeHost(config), catch: toError }), - writeStdout: (text: string) => Effect.sync(() => { process.stdout.write(text); }), - writeStderr: (text: string) => Effect.sync(() => { process.stderr.write(text); }) +function drain() { + if (!ready) return; + let index; + while ((index = input.indexOf("\n")) !== -1) { + const line = input.slice(0, index); + input = input.slice(index + 1); + if (!line.trim()) continue; + try { + const request = parseJsonRpcMessage(JSON.parse(line)); + if (!isJsonRpcRequest(request) || request.id == null || request.id === "__auth" || pending.has(request.id)) throw new Error(); + pending.set(request.id, request.method); + socket.write(`${JSON.stringify(request)}\n`); + } catch { fail(); return; } + } + finish(); +} +function start() { + ready = true; + if (!streaming) { + input = `${JSON.stringify({ jsonrpc: "2.0", id: 1, method, params: {} })}\n`; + inputEnded = true; + } + drain(); +} +socket.setEncoding("utf8"); +socket.setTimeout(35000); +socket.on("timeout", fail); +socket.on("connect", () => { + if (!useTcp) { start(); return; } + try { + const file = process.env.OPZERO_CHROME_HOST_TOKEN_FILE; + if (!file) throw new Error(); + socket.write(`${JSON.stringify({ jsonrpc: "2.0", id: "__auth", method: "host.authenticate", params: { token: fs.readFileSync(file, "utf8").trim() } })}\n`); + } catch { fail(); } }); - -const program = Effect.gen(function* () { - const io = yield* ClientIo; - const config = yield* io.readConfig(); - const response = yield* io.request(config); - yield* io.writeStdout(response); +socket.on("data", chunk => { + output += chunk; + if (Buffer.byteLength(output) > maxResponseBytes) { fail(); return; } + let index; + while ((index = output.indexOf("\n")) !== -1) { + const line = output.slice(0, index); + output = output.slice(index + 1); + try { + const response = parseJsonRpcMessage(JSON.parse(line)); + if (isJsonRpcRequest(response)) continue; + if (response.id === "__auth") { if (response.error) fail(); else start(); continue; } + if (response.id == null || !pending.has(response.id)) continue; + const requestMethod = pending.get(response.id); + pending.delete(response.id); + if (requestMethod === "privateFill") { + const value = response.result; + const filled = !response.error && typeof value === "object" && value !== null && "status" in value && value.status === "filled"; + const unsupported = !response.error && typeof value === "object" && value !== null && "status" in value && value.status === "unsupported"; + const notReady = !response.error && typeof value === "object" && value !== null && "status" in value && value.status === "not-ready"; + process.stdout.write(`${JSON.stringify({ jsonrpc: "2.0", id: response.id, result: { status: filled ? "filled" : unsupported ? "unsupported" : notReady ? "not-ready" : "not-filled-or-unknown", retry: false } })}\n`); + } else process.stdout.write(`${JSON.stringify(response)}\n`); + if (response.error) process.exitCode = 1; + } catch { fail(); return; } + } + finish(); }); - -NodeRuntime.runMain(Effect.provide(program, ClientIoLive)); +socket.on("error", fail); +socket.on("close", () => { if (pending.size || output) fail(); process.stdin.destroy(); }); +if (streaming) { + process.stdin.setEncoding("utf8"); + process.stdin.on("data", chunk => { + input += chunk; + if (Buffer.byteLength(input) > maxBytes) { fail(); return; } + drain(); + }); + process.stdin.on("end", () => { inputEnded = true; if (input.trim()) input += "\n"; drain(); }); + process.stdin.on("error", fail); +} diff --git a/src/native-host/host.ts b/src/native-host/host.ts index 7dbfeee..59303bc 100755 --- a/src/native-host/host.ts +++ b/src/native-host/host.ts @@ -4,203 +4,236 @@ import net from "node:net"; import os from "node:os"; import path from "node:path"; import process from "node:process"; -import { Context, Effect, Layer } from "effect"; -import { NodeRuntime } from "@effect/platform-node"; -import { isJsonRpcRequest, parseJsonRpcMessage } from "../shared/rpc"; - -const DEFAULT_PORT = Number(process.env.OPZERO_CHROME_HOST_PORT || 17365); -const SOCKET_PATH = process.env.OPZERO_CHROME_HOST_SOCKET || path.join(os.tmpdir(), "opzero-chrome-native-host.sock"); -const USE_TCP = process.platform === "win32" || process.env.OPZERO_CHROME_HOST_TRANSPORT === "tcp"; - -let nextExtensionId = 1; -const pendingExtensionRequests = new Map(); -const clients = new Set(); -type ParsedJsonRpcMessage = ReturnType; -type HostIoService = { - log: (message: string) => Effect.Effect; - writeNativeMessage: (message: unknown) => Effect.Effect; - writeClientLine: (client: net.Socket, message: unknown) => Effect.Effect; -}; - -class HostIo extends Context.Tag("opzero/NativeHostIo")() {} - -const HostIoLive = Layer.succeed(HostIo, { - log: (message: string) => Effect.sync(() => { - if (process.env.OPZERO_CHROME_HOST_DEBUG) process.stderr.write(`[opzero-chrome-host] ${message}\n`); - }), - writeNativeMessage: (message: unknown) => Effect.sync(() => { - const body = Buffer.from(JSON.stringify(message), "utf8"); - const header = Buffer.alloc(4); - header.writeUInt32LE(body.length, 0); - process.stdout.write(Buffer.concat([header, body])); - }), - writeClientLine: (client: net.Socket, message: unknown) => Effect.sync(() => { - client.write(`${JSON.stringify(message)}\n`); - }) -}); - -function runHostEffect(program: Effect.Effect) { - return Effect.runPromise(Effect.provide(program, HostIoLive)); +import { randomUUID, timingSafeEqual } from "node:crypto"; +import { isJsonRpcRequest, parseJsonRpcMessage, type JsonRpcMessage } from "../shared/rpc"; + +const socketPath = process.env.OPZERO_CHROME_HOST_SOCKET || path.join(os.homedir(), ".opzero-chrome", "default.sock"); +const useTcp = process.platform === "win32" || process.env.OPZERO_CHROME_HOST_TRANSPORT === "tcp"; +const port = Number(process.env.OPZERO_CHROME_HOST_PORT || 17365); +const epoch = randomUUID(); +const protocolRequestId = `protocol:${epoch}`; +let extensionProtocol: "checking" | "ready" | "unsupported" = "checking"; +const protocolTimer = setTimeout(() => { extensionProtocol = "unsupported"; }, 10000); +protocolTimer.unref(); +const maxBytes = 1024 * 1024; +const maxResponseBytes = 64 * maxBytes; +const clients = new Map; profileEvents: boolean }>(); +const pending = new Map(); +let nextId = 1; +let ownsSocket = false; +let tcpToken: Buffer | undefined; + +function native(message: unknown) { + const body = Buffer.from(JSON.stringify(message)); + if (body.length > maxBytes) return false; + const header = Buffer.alloc(4); + header.writeUInt32LE(body.length); + process.stdout.write(Buffer.concat([header, body])); + return true; } -function readNativeMessages(onMessage: (message: ParsedJsonRpcMessage) => void) { - let buffer = Buffer.alloc(0); - process.stdin.on("data", (chunk) => { - buffer = Buffer.concat([buffer, Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)]); - while (buffer.length >= 4) { - const length = buffer.readUInt32LE(0); - if (buffer.length < 4 + length) break; - const body = buffer.subarray(4, 4 + length); - buffer = buffer.subarray(4 + length); - try { - onMessage(parseJsonRpcMessage(JSON.parse(body.toString("utf8")))); - } catch (error) { - runHostEffect(Effect.flatMap(HostIo, (io) => io.log(`invalid native message: ${error instanceof Error ? error.message : String(error)}`))); - } - } - }); +function result(id: unknown, value: unknown) { return { jsonrpc: "2.0", id, result: value }; } +function error(id: unknown, message: string) { return { jsonrpc: "2.0", id, error: { code: -32000, message } }; } +function reply(socket: net.Socket, message: unknown) { + if (!socket.destroyed) socket.write(`${JSON.stringify(message)}\n`); } -function rpcResult(id: number | string | null | undefined, result: unknown) { - return { jsonrpc: "2.0", id, result }; +function release(socket: net.Socket) { + const client = clients.get(socket); + if (!client) return; + clients.delete(socket); + for (const [id, request] of pending) { + if (request.socket !== socket) continue; + clearTimeout(request.timer); + pending.delete(id); + } + if (client.authenticated && extensionProtocol === "ready") native({ jsonrpc: "2.0", method: "internal.releaseClient", params: { session_id: client.session } }); } -function rpcError(id: number | string | null | undefined, error: unknown) { - const message = error instanceof Error ? error.message : String(error || "Unknown error"); - const code = typeof error === "object" && error != null && "code" in error && Number.isInteger(error.code) ? error.code : -32000; - return { - jsonrpc: "2.0", - id, - error: { - code, - message +function handleNative(message: JsonRpcMessage) { + if (!isJsonRpcRequest(message)) { + if (message.id === protocolRequestId) { + if (extensionProtocol !== "checking") return; + clearTimeout(protocolTimer); + extensionProtocol = !message.error && typeof message.result === "object" && message.result !== null + && "protocolVersion" in message.result && message.result.protocolVersion === 2 ? "ready" : "unsupported"; + return; } - }; -} - -function handleExtensionRequest(message: ParsedJsonRpcMessage) { - return Effect.gen(function* () { - const io = yield* HostIo; - if (!isJsonRpcRequest(message)) return; - if (message.method === "ping") return yield* io.writeNativeMessage(rpcResult(message.id, "pong")); - if (message.method === "getHostInfo") { - return yield* io.writeNativeMessage(rpcResult(message.id, { - name: "opzero-chrome-native-host", - version: "0.1.2", - pid: process.pid, - transport: USE_TCP ? "tcp" : "unix", - endpoint: USE_TCP ? `127.0.0.1:${DEFAULT_PORT}` : SOCKET_PATH - })); + const request = typeof message.id === "number" ? pending.get(message.id) : undefined; + if (!request) return; + pending.delete(Number(message.id)); + clearTimeout(request.timer); + if (request.private) { + const ok = !message.error && typeof message.result === "object" && message.result !== null + && "status" in message.result && message.result.status === "filled"; + reply(request.socket, result(request.id, { status: ok ? "filled" : "not-filled-or-unknown", retry: false })); + } else { + reply(request.socket, message.error ? error(request.id, message.error.message) : result(request.id, message.result)); } - return yield* io.writeNativeMessage(rpcError(message.id, new Error(`Unsupported native host method: ${message.method}`))); - }); -} - -function broadcast(message: unknown) { - return Effect.gen(function* () { - const io = yield* HostIo; - for (const client of clients) yield* io.writeClientLine(client, message); - }); + return; + } + if (message.id != null) { + if (message.method === "ping") native(result(message.id, "pong")); + else if (message.method === "getHostInfo") native(result(message.id, { + name: "opzero-chrome-native-host", version: "0.2.0", protocolVersion: 2, extensionProtocol, epoch, pid: process.pid, + transport: useTcp ? "tcp" : "unix", endpoint: useTcp ? `127.0.0.1:${port}` : socketPath + })); + else native(error(message.id, "Unsupported native host method")); + return; + } + if (extensionProtocol !== "ready") return; + const params = typeof message.params === "object" && message.params !== null ? message.params : {}; + const session = "session_id" in params ? params.session_id : undefined; + for (const [socket, client] of clients) { + if (!client.authenticated) continue; + if (typeof session === "string" ? client.session === session + : message.method === "onControlStopped" || (message.method === "onDownloadChange" && client.profileEvents)) { + reply(socket, message); + } + } + if (message.method === "onControlStopped") { + for (const socket of clients.keys()) { release(socket); socket.end(); } + } } -function handleNativeMessage(message: ParsedJsonRpcMessage) { - return Effect.gen(function* () { - const io = yield* HostIo; - if ("id" in message && ("result" in message || "error" in message)) { - if (message.id == null) return; - const pending = pendingExtensionRequests.get(message.id); - if (!pending) return; - pendingExtensionRequests.delete(message.id); - clearTimeout(pending.timer); - const response = message.error ? rpcError(pending.clientId, new Error(message.error.message)) : rpcResult(pending.clientId, message.result); - yield* io.writeClientLine(pending.client, response); - return; - } - if (isJsonRpcRequest(message) && "id" in message) { - yield* handleExtensionRequest(message); +function handleClient(socket: net.Socket, message: JsonRpcMessage) { + const client = clients.get(socket); + if (!client) return; + if (!isJsonRpcRequest(message) || (typeof message.id !== "number" && typeof message.id !== "string")) { + reply(socket, error(null, "Expected JSON-RPC request with a non-null id")); + return; + } + const id = JSON.stringify(message.id); + if (client.ids.has(id)) { + reply(socket, error(message.id, "Duplicate id; action not replayed")); + return; + } + if (client.ids.size >= 10000) { + reply(socket, error(message.id, "Connection request limit reached; no action dispatched; connection revoked")); + release(socket); + socket.end(); + return; + } + client.ids.add(id); + const params = typeof message.params === "object" && message.params !== null && !Array.isArray(message.params) ? message.params : {}; + if (!client.authenticated) { + const token = "token" in params && typeof params.token === "string" ? Buffer.from(params.token) : Buffer.alloc(0); + if (message.method !== "host.authenticate" || !tcpToken || token.length !== tcpToken.length || !timingSafeEqual(token, tcpToken)) { + reply(socket, error(message.id, "Authentication required")); + socket.end(); return; } - if (isJsonRpcRequest(message)) yield* broadcast(message); - }); + client.authenticated = true; + reply(socket, result(message.id, { authenticated: true })); + return; + } + if (message.method === "host.ping") { reply(socket, result(message.id, "pong")); return; } + if (message.method === "host.info") { + reply(socket, result(message.id, { protocolVersion: 2, extensionProtocol, epoch, session_id: client.session, reconnect: "new-session-no-replay" })); + return; + } + if (message.method === "host.subscribeProfileEvents") { + client.profileEvents = "enabled" in params && params.enabled === true; + reply(socket, result(message.id, { enabled: client.profileEvents })); + return; + } + if (message.method.startsWith("internal.") || message.method.startsWith("host.")) { + reply(socket, error(message.id, "Reserved method")); + return; + } + if (extensionProtocol !== "ready") { + reply(socket, message.method === "privateFill" + ? result(message.id, { status: extensionProtocol === "checking" ? "not-ready" : "unsupported", retry: false }) + : error(message.id, "Extension protocol v2 is not ready; no action dispatched")); + return; + } + const extensionId = nextId++; + const timer = setTimeout(() => { + pending.delete(extensionId); + reply(socket, error(message.id, "Outcome unknown; connection revoked; do not replay")); + release(socket); + socket.end(); + }, Number(process.env.OPZERO_CHROME_REQUEST_TIMEOUT_MS || 30000)); + pending.set(extensionId, { socket, id: message.id, private: message.method === "privateFill", timer }); + const sent = native({ jsonrpc: "2.0", id: extensionId, method: message.method, + params: { ...params, session_id: client.session, sessionId: client.session, turn_id: epoch, turnId: epoch } }); + if (!sent) { + clearTimeout(timer); + pending.delete(extensionId); + reply(socket, error(message.id, "Request exceeds native frame limit; no action dispatched")); + } } -function parseClientLines(socket: net.Socket, onMessage: (message: ParsedJsonRpcMessage) => void) { - let text = ""; +const server = net.createServer(socket => { + clients.set(socket, { session: randomUUID(), authenticated: !useTcp, ids: new Set(), profileEvents: false }); + socket.on("close", () => release(socket)); + socket.on("error", () => { release(socket); socket.destroy(); }); socket.setEncoding("utf8"); - socket.on("data", (chunk) => { + let text = ""; + socket.on("data", chunk => { text += chunk; + if (Buffer.byteLength(text) > maxBytes) { socket.destroy(); return; } let index; while ((index = text.indexOf("\n")) !== -1) { - const line = text.slice(0, index).trim(); + const line = text.slice(0, index); text = text.slice(index + 1); - if (!line) continue; - try { - onMessage(parseJsonRpcMessage(JSON.parse(line))); - } catch (error) { - const message = error instanceof Error ? error.message : String(error); - runHostEffect(Effect.flatMap(HostIo, (io) => io.writeClientLine(socket, rpcError(null, new Error(`Invalid JSON: ${message}`))))); - } + if (!line.trim()) continue; + try { handleClient(socket, parseJsonRpcMessage(JSON.parse(line))); } + catch { reply(socket, error(null, "Invalid JSON-RPC request")); } } }); -} +}); -function attachClient(socket: net.Socket) { - clients.add(socket); - socket.on("close", () => clients.delete(socket)); - socket.on("error", () => clients.delete(socket)); - parseClientLines(socket, (message) => { - runHostEffect(handleClientMessage(socket, message)); - }); +function shutdown(code: number) { + for (const socket of clients.keys()) socket.destroy(); + if (ownsSocket) { try { fs.unlinkSync(socketPath); } catch {} } + process.exit(code); } -function handleClientMessage(socket: net.Socket, message: ParsedJsonRpcMessage) { - return Effect.gen(function* () { - const io = yield* HostIo; - if (!isJsonRpcRequest(message) || !("id" in message)) { - yield* io.writeClientLine(socket, rpcError("id" in message ? message.id : null, new Error("Expected JSON-RPC request with id"))); - return; - } - if (message.method === "host.ping") { - yield* io.writeClientLine(socket, rpcResult(message.id, "pong")); - return; - } - const extensionId = nextExtensionId++; - const timer = setTimeout(() => { - pendingExtensionRequests.delete(extensionId); - runHostEffect(Effect.flatMap(HostIo, (io) => io.writeClientLine(socket, rpcError(message.id, new Error(`Extension request ${message.method} timed out`))))); - }, Number(process.env.OPZERO_CHROME_REQUEST_TIMEOUT_MS || 30000)); - pendingExtensionRequests.set(extensionId, { client: socket, clientId: message.id ?? null, timer }); - yield* io.writeNativeMessage({ - jsonrpc: "2.0", - id: extensionId, - method: message.method, - params: message.params || {} - }); - }); -} +server.on("error", () => { + process.stderr.write("Native endpoint unavailable; no existing endpoint was replaced\n"); + shutdown(1); +}); -function startServer() { - const server = net.createServer(attachClient); - server.on("error", (error) => { - runHostEffect(Effect.flatMap(HostIo, (io) => io.log(`server error: ${error.message}`))); - process.exitCode = 1; - }); - if (USE_TCP) { - server.listen(DEFAULT_PORT, "127.0.0.1", () => runHostEffect(Effect.flatMap(HostIo, (io) => io.log(`listening on 127.0.0.1:${DEFAULT_PORT}`)))); - return; - } - try { - if (fs.existsSync(SOCKET_PATH)) fs.unlinkSync(SOCKET_PATH); - server.listen(SOCKET_PATH, () => runHostEffect(Effect.flatMap(HostIo, (io) => io.log(`listening on ${SOCKET_PATH}`)))); - } catch (error) { - runHostEffect(Effect.flatMap(HostIo, (io) => io.log(`socket setup failed: ${error instanceof Error ? error.message : String(error)}`))); +try { + if (useTcp) { + const file = process.env.OPZERO_CHROME_HOST_TOKEN_FILE; + if (!file) throw new Error("token file required"); + const stat = fs.lstatSync(file); + if (!stat.isFile() || (process.platform !== "win32" && (stat.mode & 0o077) !== 0)) throw new Error("private token file required"); + tcpToken = Buffer.from(fs.readFileSync(file, "utf8").trim()); + if (tcpToken.length < 32) throw new Error("token too short"); + server.listen(port, "127.0.0.1"); + } else { + process.umask(0o077); + const directory = path.dirname(socketPath); + fs.mkdirSync(directory, { recursive: true, mode: 0o700 }); + const stat = fs.lstatSync(directory); + if (!stat.isDirectory() || stat.uid !== process.getuid?.() || (stat.mode & 0o077) !== 0) throw new Error("private socket directory required"); + if (fs.existsSync(socketPath)) throw new Error("endpoint exists"); + server.listen(socketPath, () => { ownsSocket = true; fs.chmodSync(socketPath, 0o600); }); } +} catch { + process.stderr.write("Native endpoint setup refused; use a private directory or authenticated TCP\n"); + shutdown(1); } -NodeRuntime.runMain(Effect.gen(function* () { - process.stdin.on("end", () => process.exit(0)); - process.stdin.on("error", () => process.exit(1)); - readNativeMessages((message) => runHostEffect(handleNativeMessage(message))); - startServer(); - yield* Effect.never; -}).pipe(Effect.provide(HostIoLive))); +let buffer = Buffer.alloc(0); +process.stdin.on("data", chunk => { + buffer = Buffer.concat([buffer, Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)]); + while (buffer.length >= 4) { + const length = buffer.readUInt32LE(0); + if (length > maxResponseBytes) shutdown(1); + if (buffer.length < length + 4) break; + const body = buffer.subarray(4, length + 4); + buffer = buffer.subarray(length + 4); + try { handleNative(parseJsonRpcMessage(JSON.parse(body.toString("utf8")))); } + catch { /* Malformed native frames are never echoed. */ } + } +}); +process.stdin.on("end", () => shutdown(0)); +process.stdin.on("error", () => shutdown(1)); +process.stdout.on("error", () => shutdown(1)); +process.on("SIGTERM", () => shutdown(0)); +process.on("SIGINT", () => shutdown(0)); +native({ jsonrpc: "2.0", id: protocolRequestId, method: "getInfo", params: {} }); diff --git a/src/native-host/transport.ts b/src/native-host/transport.ts new file mode 100644 index 0000000..fbeeb12 --- /dev/null +++ b/src/native-host/transport.ts @@ -0,0 +1,205 @@ +import fs from "node:fs/promises"; +import net from "node:net"; +import path from "node:path"; +import { createHash } from "node:crypto"; +import { execFile } from "node:child_process"; +import { promisify } from "node:util"; +import { parseJsonRpcMessage, isJsonRpcRequest } from "../shared/rpc"; +import { parseObservation, type Observation } from "../shared/page-protocol"; +export type { Observation } from "../shared/page-protocol"; + +const exec = promisify(execFile); +const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)); +function object(value: unknown): Record { + if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error("Invalid transport response"); + return value as Record; +} +function string(value: unknown): string { if (typeof value !== "string") throw new Error("Invalid string response"); return value; } +export type OwnedPage = Readonly<{ tabId: number; origin: string }>; +export type PrivateSubmission = Readonly<{ submitToken: string; documentId: string }>; +export type ActionOutcome = { status: "executed" | "not-executed" | "unknown"; retry: false; reason?: string }; +export type RecordingReceipt = { path: string | null; directory: string; seconds: number; frames: { file: string; seconds: number; sha256: string }[]; error: string | null; encodeMs: number; captureMs: number; sampleFps: number }; + +export class ChromeTransport { + #socket: net.Socket; + #next = 0; + #pending = new Map void; reject: (error: Error) => void; timer: ReturnType }>(); + #pages = new Map(); + #recordings = new Map Promise }>(); + readonly session: string; + readonly epoch: string; + + private constructor(socket: net.Socket, session: string, epoch: string) { + this.#socket = socket; this.session = session; this.epoch = epoch; + } + + static async connect(socketPath: string) { + const directory = await fs.lstat(path.dirname(socketPath)); + const endpoint = await fs.lstat(socketPath); + if (!directory.isDirectory() || directory.uid !== process.getuid?.() || (directory.mode & 0o077) !== 0 || !endpoint.isSocket() || endpoint.uid !== process.getuid?.()) throw new Error("Explicit private owned Unix socket required"); + const socket = net.createConnection(socketPath); + await new Promise((resolve, reject) => { socket.once("connect", resolve); socket.once("error", reject); }); + const transport = new ChromeTransport(socket, "", ""); + let text = ""; + socket.setEncoding("utf8"); + socket.on("data", chunk => { + text += chunk; + if (Buffer.byteLength(text) > 64 * 1024 * 1024) { transport.#fail(); return; } + let index; + while ((index = text.indexOf("\n")) >= 0) { + const line = text.slice(0, index); text = text.slice(index + 1); + try { + const response = parseJsonRpcMessage(JSON.parse(line)); + if (isJsonRpcRequest(response) || typeof response.id !== "number") continue; + const pending = transport.#pending.get(response.id); + if (!pending) continue; + clearTimeout(pending.timer); transport.#pending.delete(response.id); + if (response.error) pending.reject(new Error(response.error.message)); else pending.resolve(response.result); + } catch { transport.#fail(); } + } + }); + socket.on("error", () => transport.#fail()); socket.on("close", () => transport.#fail()); + try { + const info = object(await transport.#call("host.info")); + if (info.protocolVersion !== 2 || info.extensionProtocol !== "ready") throw new Error("Extension protocol not ready; connect again explicitly without replaying inputs"); + const extension = object(await transport.#call("getInfo")); + if (extension.protocolVersion !== 2 || extension.pageProtocolVersion !== 2) throw new Error("Typed page protocol version 2 required"); + Object.defineProperties(transport, { session: { value: string(info.session_id) }, epoch: { value: string(info.epoch) } }); + return transport; + } catch (error) { transport.#fail(); throw error; } + } + + #fail() { + this.#socket.destroy(); this.#pages.clear(); + for (const request of this.#pending.values()) { clearTimeout(request.timer); request.reject(new Error("Transport closed; outcome unknown; no replay")); } + this.#pending.clear(); + } + #call(method: string, params: Record = {}): Promise { + if (this.#socket.destroyed) return Promise.reject(new Error("Transport closed; no replay")); + const id = ++this.#next; + return new Promise((resolve, reject) => { + const timer = setTimeout(() => this.#fail(), 35000); + this.#pending.set(id, { resolve, reject, timer }); + this.#socket.write(JSON.stringify({ jsonrpc: "2.0", id, method, params }) + "\n"); + }); + } + #owned(page: OwnedPage) { + if (this.#pages.get(page.tabId) !== page || this.#socket.destroyed) throw new Error("Page is not owned by this connection"); + return { tabId: page.tabId }; + } + async open(url: string, options: { allowInsecureLoopback?: boolean } = {}): Promise { + const origin = new URL(url).origin; + const result = object(await this.#call("createTab")); + if (typeof result.id !== "number" || result.active !== false) throw new Error("Inactive page creation failed"); + const page = Object.freeze({ tabId: result.id, origin }); this.#pages.set(page.tabId, page); + await this.#call("attach", { tabId: page.tabId }); + await this.#call("bindPage", { tabId: page.tabId, expectedOrigin: origin, ...options }); + await this.navigate(page, url); + return page; + } + async navigate(page: OwnedPage, url: string) { + if (new URL(url).origin !== page.origin) throw new Error("Origin change refused"); + await this.#call("navigatePage", { ...this.#owned(page), url }); + } + async observe(page: OwnedPage, options: { controlsOnly?: boolean } = {}): Promise { + if (options.controlsOnly !== undefined && typeof options.controlsOnly !== "boolean") throw new Error("Invalid observation mode"); + const result = parseObservation(await this.#call("observePage", { ...this.#owned(page), controlsOnly: options.controlsOnly === true })); + if (new URL(result.url).origin !== page.origin || result.mode !== (options.controlsOnly ? "controls-only" : "full")) throw new Error("Observation binding changed"); + return result; + } + async waitFor(page: OwnedPage, expect: { text?: string; url?: string }, timeoutMs = 10000): Promise { + if ((!expect.text && !expect.url) || timeoutMs < 1 || timeoutMs > 15000) throw new Error("Bounded nonempty expectation required"); + const deadline = performance.now() + timeoutMs; + while (true) { + try { + const snapshot = await this.observe(page); + if ((!expect.text || snapshot.text.includes(expect.text)) && (!expect.url || snapshot.url === expect.url)) return snapshot; + } catch (error) { + if (!(error instanceof Error) || !["Page origin not ready or mismatch", "Frame with ID 0 was removed.", "Capture or observation blocked: private fields or frames", "Private document quarantined until cross-document navigation"].includes(error.message)) throw error; + } + if (performance.now() >= deadline) throw new Error("Read-only wait timed out; no input replayed"); + await sleep(50); + } + } + async act(page: OwnedPage, snapshot: string, actionId: string, text?: string): Promise { + const result = object(await this.#call("actPage", { ...this.#owned(page), snapshot, actionId, ...(text === undefined ? {} : { text }) })); + if (result.status !== "executed" && result.status !== "not-executed" && result.status !== "unknown") throw new Error("Invalid action result; no replay"); + return { status: result.status, retry: false, ...(typeof result.reason === "string" ? { reason: result.reason } : {}) }; + } + async privateFill(page: OwnedPage, selectors: string[], values: string[], options: { allowInsecureLoopback?: boolean } = {}) { + if (this.#recordings.has(page.tabId)) throw new Error("Stop recording before private input"); + const params = this.#owned(page); + const observed = object(await this.#call("observeDocument", { ...params, expectedOrigin: page.origin, selectors, ...options })); + const result = object(await this.#call("privateFill", { ...params, expectedOrigin: page.origin, token: string(observed.token), documentId: string(observed.documentId), values })); + return { status: result.status === "filled" ? "filled" : "not-filled-or-unknown", retry: false } as const; + } + async preparePrivateSubmit(page: OwnedPage, snapshot: string, actionId: string): Promise { + const result = object(await this.#call("preparePrivateSubmit", { ...this.#owned(page), snapshot, actionId })); + if (result.status !== "prepared") throw new Error("Private submit preparation refused"); + return Object.freeze({ submitToken: string(result.submitToken), documentId: string(result.documentId) }); + } + async submitPrivate(page: OwnedPage, submission: PrivateSubmission): Promise { + const result = object(await this.#call("submitPrivate", { ...this.#owned(page), submitToken: submission.submitToken, documentId: submission.documentId })); + if (result.status !== "executed" && result.status !== "not-executed" && result.status !== "unknown") throw new Error("Invalid private submit result; no replay"); + return { status: result.status, retry: false }; + } + async screenshot(page: OwnedPage): Promise { + const result = object(await this.#call("capturePage", this.#owned(page))); + const data = Buffer.from(string(result.data), "base64"); + if (data.subarray(0, 3).toString("hex") !== "ffd8ff") throw new Error("Invalid JPEG response"); + return data; + } + async startRecording(page: OwnedPage, artifactRoot: string, options: { fps?: number; maxSeconds?: number } = {}) { + const fps = options.fps ?? 5, maxSeconds = options.maxSeconds ?? 30; + if (!Number.isInteger(fps) || fps < 1 || fps > 15 || !Number.isFinite(maxSeconds) || maxSeconds < 1 || maxSeconds > 60 || this.#recordings.has(page.tabId)) throw new Error("Invalid or duplicate recording"); + const stat = await fs.lstat(artifactRoot); + if (!stat.isDirectory() || stat.uid !== process.getuid?.() || (stat.mode & 0o077) !== 0) throw new Error("Owned private artifact directory required"); + const directory = await fs.mkdtemp(path.join(artifactRoot, "tab-video-")); + await fs.chmod(directory, 0o700); + await this.#call("recordingState", { ...this.#owned(page), active: true }); + const frames: RecordingReceipt["frames"] = []; + const start = performance.now(); let stopped = false, error: string | null = null, bytes = 0, ended = start, captureMs = 0; + const capture = async () => { + const began = performance.now(); + try { + const data = await this.screenshot(page); + bytes += data.length; if (bytes > 100 * 1024 * 1024) throw new Error("Recording storage limit"); + const file = `${String(frames.length).padStart(5, "0")}.jpg`; + await fs.writeFile(path.join(directory, file), data, { mode: 0o600 }); + frames.push({ file, seconds: (performance.now() - start) / 1000, sha256: createHash("sha256").update(data).digest("hex") }); + } finally { captureMs += performance.now() - began; } + }; + let stopPromise: Promise | undefined; + const record = { stop: () => stopPromise ??= (async () => { + stopped = true; await loop; + try { await this.#call("recordingState", { ...this.#owned(page), active: false }); } catch { error ??= "Transport unavailable; recording incomplete"; } + this.#recordings.delete(page.tabId); + const seconds = Math.max(1 / 30, (ended - start) / 1000 - (frames[0]?.seconds ?? 0)); + const encodeStart = performance.now(); let output: string | null = null; + if (frames.length) { + const concat = frames.map((frame, i) => `file '${frame.file}'\nduration ${Math.max(0.001, (frames[i + 1]?.seconds ?? (ended - start) / 1000) - frame.seconds).toFixed(6)}`).join("\n") + `\nfile '${frames.at(-1)?.file}'\n`; + await fs.writeFile(path.join(directory, "frames.ffconcat"), concat, { mode: 0o600 }); + try { + await exec("ffmpeg", ["-v", "error", "-y", "-f", "concat", "-safe", "1", "-i", "frames.ffconcat", "-vf", "fps=30,pad=ceil(iw/2)*2:ceil(ih/2)*2", "-t", String(seconds), "-c:v", "libx264", "-pix_fmt", "yuv420p", "-movflags", "+faststart", "recording.mp4"], { cwd: directory, timeout: 60000, env: { PATH: process.env.PATH } }); + output = path.join(directory, "recording.mp4"); await fs.chmod(output, 0o600); + } catch { error ??= "Encoding failed; JPEG frames preserved"; } + } + const receipt = { path: output, directory, seconds, frames, error, encodeMs: performance.now() - encodeStart, captureMs, sampleFps: fps }; + await fs.writeFile(path.join(directory, "capture.json"), JSON.stringify(receipt, null, 2), { mode: 0o600 }); + return receipt; + })() }; + const loop = (async () => { + try { do { await capture(); if (stopped) break; await sleep(1000 / fps); } while (!stopped && performance.now() - start < maxSeconds * 1000); } + catch { error = "Capture interrupted: privacy, ownership, navigation, or transport changed"; } + finally { ended = performance.now(); } + })(); + this.#recordings.set(page.tabId, record); + return record; + } + async close() { + try { + for (const recording of this.#recordings.values()) await recording.stop(); + if (!this.#socket.destroyed) await this.#call("finalizeTabs", { keep: [] }); + } finally { this.#fail(); } + } +} diff --git a/src/shared/page-protocol.ts b/src/shared/page-protocol.ts new file mode 100644 index 0000000..b5c4837 --- /dev/null +++ b/src/shared/page-protocol.ts @@ -0,0 +1,34 @@ +export type PageAction = { id: string; kind: "fill" | "click"; label: string; role: string; disabled: boolean }; +export type Observation = { + status: "observed"; pageProtocolVersion: 2; snapshot: string; url: string; title: string; text: string; actions: PageAction[]; + mode: "full" | "controls-only"; partial: boolean; + opaqueSurfaces: { id: string; kind: "iframe" | "frame" | "object" | "embed" | "closed-shadow-root" }[]; + truncation: { text: boolean; actions: boolean; opaqueSurfaces: boolean; labels: boolean; title: boolean }; +}; + +export function parseObservation(value: unknown): Observation { + const fail = (): never => { throw new Error("Invalid page protocol v2 observation"); }; + const object = (v: unknown): Record => typeof v === "object" && v !== null && !Array.isArray(v) ? v as Record : fail(); + const string = (v: unknown, limit: number): string => typeof v === "string" && v.length <= limit ? v : fail(); + const bool = (v: unknown): boolean => typeof v === "boolean" ? v : fail(); + const raw = object(value), truncation = object(raw.truncation); + if (raw.status !== "observed" || raw.pageProtocolVersion !== 2 || raw.mode !== "full" && raw.mode !== "controls-only" + || !Array.isArray(raw.actions) || raw.actions.length > 100 || !Array.isArray(raw.opaqueSurfaces) || raw.opaqueSurfaces.length > 100) fail(); + const actions = (raw.actions as unknown[]).map((value): PageAction => { + const a = object(value); + if (a.kind !== "fill" && a.kind !== "click") return fail(); + return { id: string(a.id, 100), kind: a.kind, label: string(a.label, 160), role: string(a.role, 80), disabled: bool(a.disabled) }; + }); + const opaqueSurfaces = (raw.opaqueSurfaces as unknown[]).map((value, index) => { + const o = object(value); + if (Object.keys(o).length !== 2 || o.id !== `opaque-${index}` || typeof o.kind !== "string" || !["iframe", "frame", "object", "embed", "closed-shadow-root"].includes(o.kind)) return fail(); + return { id: string(o.id, 100), kind: o.kind as Observation["opaqueSurfaces"][number]["kind"] }; + }); + const flags = { text: bool(truncation.text), actions: bool(truncation.actions), opaqueSurfaces: bool(truncation.opaqueSurfaces), labels: bool(truncation.labels), title: bool(truncation.title) }; + const text = string(raw.text, 12000), snapshot = string(raw.snapshot, 200), partial = bool(raw.partial); + if (!snapshot || actions.some(a => !a.id) || new Set(actions.map(a => a.id)).size !== actions.length + || partial !== (opaqueSurfaces.length > 0) || flags.opaqueSurfaces && opaqueSurfaces.length !== 100 + || raw.mode === "controls-only" && (text !== "" || flags.text)) fail(); + return { status: "observed", pageProtocolVersion: 2, snapshot, url: string(raw.url, 8192), title: string(raw.title, 200), text, actions, + mode: raw.mode as Observation["mode"], partial, opaqueSurfaces, truncation: flags }; +} diff --git a/tests/acceptance/distribution.test.ts b/tests/acceptance/distribution.test.ts index e87efe4..e2239c8 100644 --- a/tests/acceptance/distribution.test.ts +++ b/tests/acceptance/distribution.test.ts @@ -5,6 +5,7 @@ import os from "node:os"; import path from "node:path"; import { fileURLToPath } from "node:url"; import { describe, expect, it } from "vitest"; +import { testTemp } from "../support/temp"; const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../.."); @@ -131,7 +132,7 @@ describe("Chrome Control distribution", () => { }); it("installs and validates a native host manifest using the packaged skill", async () => { - const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "opzero-chrome-test-")); + const tempDir = testTemp(); const skillDir = path.join(tempDir, "chrome-control"); copyDir(path.join(root, "dist/skill/chrome-control"), skillDir); const manifestPath = path.join(tempDir, "com.opzero.chrome.json"); @@ -168,7 +169,7 @@ describe("Chrome Control distribution", () => { }); it("reports a repair command for an invalid native host manifest", async () => { - const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "opzero-chrome-test-")); + const tempDir = testTemp(); const skillDir = path.join(tempDir, "chrome-control"); copyDir(path.join(root, "dist/skill/chrome-control"), skillDir); const manifestPath = path.join(tempDir, "com.opzero.chrome.json"); @@ -203,7 +204,7 @@ describe("Chrome Control distribution", () => { }); it("detects extensions registered in Chrome Secure Preferences", async () => { - const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "opzero-chrome-test-")); + const tempDir = testTemp(); const profileDir = path.join(tempDir, "Default"); fs.mkdirSync(profileDir, { recursive: true }); fs.writeFileSync(path.join(tempDir, "Local State"), `${JSON.stringify({ profile: { last_used: "Default" } })}\n`); @@ -238,8 +239,10 @@ describe("Chrome Control distribution", () => { }); it("responds to native messaging ping frames", async () => { + const tempDir = testTemp(); const child = spawn(process.execPath, ["dist/native-host/host.js"], { cwd: root, + env: { ...process.env, OPZERO_CHROME_HOST_SOCKET: path.join(tempDir, "s") }, stdio: ["pipe", "pipe", "pipe"] }); @@ -252,12 +255,20 @@ describe("Chrome Control distribution", () => { child.stdout.on("data", (chunk) => { buffer = Buffer.concat([buffer, chunk]); - if (buffer.length < 4) return; - const length = buffer.readUInt32LE(0); - if (buffer.length < 4 + length) return; - clearTimeout(timeout); - child.kill(); - resolve(JSON.parse(buffer.subarray(4, 4 + length).toString("utf8"))); + while (buffer.length >= 4 && buffer.length >= 4 + buffer.readUInt32LE(0)) { + const length = buffer.readUInt32LE(0); + const message = JSON.parse(buffer.subarray(4, 4 + length).toString("utf8")); + buffer = buffer.subarray(4 + length); + if (message.method === "getInfo") { + const body = Buffer.from(JSON.stringify({ jsonrpc: "2.0", id: message.id, result: { protocolVersion: 2 } })); + const header = Buffer.alloc(4); header.writeUInt32LE(body.length); + child.stdin.write(Buffer.concat([header, body])); + continue; + } + clearTimeout(timeout); + child.kill(); + resolve(message); + } }); child.on("error", reject); @@ -271,7 +282,7 @@ describe("Chrome Control distribution", () => { }); it("lets pnpm-style script forwarding call the built client", async () => { - const socketPath = path.join(os.tmpdir(), `opzero-chrome-client-test-${process.pid}.sock`); + const socketPath = path.join(os.tmpdir(), "opencode", `oc-${process.pid}.sock`); fs.rmSync(socketPath, { force: true }); const server = net.createServer(); diff --git a/tests/security/background.test.ts b/tests/security/background.test.ts new file mode 100644 index 0000000..aa4037c --- /dev/null +++ b/tests/security/background.test.ts @@ -0,0 +1,247 @@ +import { expect, it, vi } from "vitest"; +import { background } from "../support/background"; + +it("preserves fixed refusal reasons across capture and recording", async () => { + const b = await background(); + const owner = { session_id: "owner", turn_id: "turn", tabId: 1, expectedOrigin: "https://synthetic.invalid" }; + await b.rpc("createTab", owner); + await b.rpc("bindPage", owner); + for (const reason of ["private-quarantine", "populated-private-input", "restored-private-selector", "invalid-private-selectors", "synthetic-secret"]) { + b.chrome.scripting.executeScript.mockImplementation((_: unknown, cb: Function) => cb([{ documentId: "doc-1", frameId: 0, result: { status: "checked", allowed: false, reason } }])); + for (const method of ["capturePage", "recordingState"]) { + expect((await b.rpc(method, { ...owner, active: true })).error.message).toBe(reason === "synthetic-secret" ? "Capture privacy check failed" : reason); + } + } + expect(b.chrome.debugger.sendCommand).not.toHaveBeenCalled(); +}); + +it("captures and starts recording without an author shadow tree eligibility scan", async () => { + const b = await background(); + const owner = { session_id: "owner", turn_id: "turn", tabId: 1, expectedOrigin: "https://synthetic.invalid" }; + await b.rpc("createTab", owner); + await b.rpc("bindPage", owner); + await b.rpc("attach", owner); + b.chrome.scripting.executeScript.mockImplementation((_: unknown, cb: Function) => cb([{ documentId: "doc-1", frameId: 0, result: { status: "checked", allowed: true } }])); + for (const shadowRootType of ["open", "closed"]) { + b.chrome.debugger.sendCommand.mockImplementation((_: unknown, method: string, ___: unknown, cb: Function) => cb(method === "DOM.getDocument" ? { root: { children: [{ shadowRootType }] } } : { data: "synthetic-jpeg" })); + for (const method of ["capturePage", "recordingState"]) { + expect((await b.rpc(method, { ...owner, active: true })).error).toBeUndefined(); + } + expect((await b.rpc("capturePage", owner)).result.data).toBe("synthetic-jpeg"); + expect((await b.rpc("recordingState", { ...owner, active: false })).error).toBeUndefined(); + } + expect(b.chrome.debugger.sendCommand.mock.calls.some(call => call[1] === "DOM.getDocument")).toBe(false); +}); + +it("discards a screenshot when its document changes during capture", async () => { + const b = await background(); + const owner = { session_id: "owner", turn_id: "turn", tabId: 1, expectedOrigin: "https://synthetic.invalid" }; + await b.rpc("createTab", owner); + await b.rpc("bindPage", owner); + await b.rpc("attach", owner); + let document = 0; + b.chrome.scripting.executeScript.mockImplementation((_: unknown, cb: Function) => cb([{ documentId: `doc-${document++}`, frameId: 0, result: { status: "checked", allowed: true } }])); + const response = await b.rpc("capturePage", owner); + expect(response.error.message).toBe("Document changed during capture; image discarded"); + expect(response.result).toBeUndefined(); +}); + +it("prioritizes persisted and legacy quarantine over embedded and shadow surfaces", async () => { + const b = await background(); + const owner = { session_id: "owner", turn_id: "turn", tabId: 1, expectedOrigin: "https://synthetic.invalid", active: true }; + await b.rpc("createTab", owner); + await b.rpc("bindPage", owner); + b.chrome.scripting.executeScript.mockImplementation((_: unknown, cb: Function) => cb([{ documentId: "doc-1", frameId: 0, result: { status: "checked", allowed: true } }])); + for (const quarantine of ["legacy-doc", { documentId: "doc-1", selectors: [] }]) { + b.chrome.storage.local.set({ "PRIVATE_CAPTURE_QUARANTINE:1": quarantine }, () => {}); + for (const method of ["observePage", "capturePage", "recordingState"]) { + expect((await b.rpc(method, owner)).error.message).toBe("private-quarantine"); + } + } + expect(b.chrome.debugger.sendCommand).not.toHaveBeenCalled(); +}); + +it("observes scoped DOM without invoking capture/CDP and carries restored selectors into the exact document", async () => { + const b = await background(); + const owner = { session_id: "owner", turn_id: "turn", tabId: 1, expectedOrigin: "https://synthetic.invalid" }; + await b.rpc("createTab", owner); await b.rpc("bindPage", owner); + b.chrome.storage.local.set({ "PRIVATE_CAPTURE_QUARANTINE:1": { documentId: "old-doc", selectors: ["#restored"] } }, () => {}); + b.chrome.scripting.executeScript.mockImplementation((request: any, cb: Function) => cb([{ documentId: "new-doc", frameId: 0, result: request.args[0] === "document-check" ? { status: "checked" } : { status: "observed", partial: true, opaqueSurfaces: [{ id: "opaque-0", kind: "iframe" }] } }])); + expect((await b.rpc("observePage", { ...owner, controlsOnly: true })).result.partial).toBe(true); + expect(b.chrome.scripting.executeScript.mock.calls[1][0]).toMatchObject({ target: { tabId: 1, documentIds: ["new-doc"] }, args: ["observe", owner.expectedOrigin, expect.any(String), "", null, { selectors: ["#restored"], controlsOnly: true }] }); + expect(b.chrome.debugger.sendCommand).not.toHaveBeenCalled(); +}); + +it("rescans restored selectors on observation and action with no public quarantine bypass", async () => { + const b = await background(); + const owner = { session_id: "owner", turn_id: "turn", tabId: 1, expectedOrigin: "https://synthetic.invalid" }; + await b.rpc("createTab", owner); await b.rpc("bindPage", owner); + b.chrome.scripting.executeScript.mockImplementation((request: any, cb: Function) => cb([{ documentId: "doc-1", frameId: 0, result: request.args[0] === "document-check" ? { status: "checked" } : { status: "observed", snapshot: request.args[2] } }])); + const snapshot = (await b.rpc("observePage", owner)).result.snapshot; + b.chrome.storage.local.set({ "PRIVATE_CAPTURE_QUARANTINE:1": { documentId: "doc-1", selectors: [] } }, () => {}); + expect((await b.rpc("actPage", { ...owner, snapshot, actionId: "0" })).result.status).toBe("not-executed"); + expect((await b.rpc("actPage", { ...owner, snapshot, actionId: "0" })).result.status).toBe("not-executed"); + expect(b.chrome.scripting.executeScript.mock.calls.some(([r]: any) => r.args[0] === "act")).toBe(false); +}); + +it("creates an inactive tab without activating a window", async () => { + const b = await background(); + const response = await b.rpc("createTab", { session_id: "owner", turn_id: "turn" }); + expect(response.error).toBeUndefined(); + expect(b.chrome.tabs.create.mock.calls[0][0].active).toBe(false); +}); + +it("advertises protocol v2 and gates profile reads after revocation", async () => { + const b = await background(); + expect((await b.rpc("getInfo")).result.protocolVersion).toBe(2); + expect((await b.rpc("getInfo")).result.pageProtocolVersion).toBe(2); + const params = { session_id: "owner", turn_id: "turn" }; + await b.rpc("createTab", params); + b.port.onMessage.emit({ jsonrpc: "2.0", method: "internal.releaseClient", params }); + expect((await b.rpc("getUserTabs", params)).error).toBeDefined(); + expect((await b.rpc("getUserHistory", params)).error).toBeDefined(); + expect(b.chrome.history.search).not.toHaveBeenCalled(); + expect((await b.rpc("constructor", params)).error.message).toContain("Unsupported browser command"); +}); + +it("refuses non-TLS private observations except opted-in loopback", async () => { + const b = await background(); + const params = { session_id: "owner", turn_id: "turn", tabId: 1, selectors: ["#password"] }; + await b.rpc("createTab", params); + expect((await b.rpc("observeDocument", { ...params, expectedOrigin: "http://synthetic.invalid" })).error).toBeDefined(); + expect((await b.rpc("observeDocument", { ...params, expectedOrigin: "http://127.0.0.1" })).error).toBeDefined(); + expect(b.chrome.scripting.executeScript).not.toHaveBeenCalled(); + b.chrome.scripting.executeScript.mockImplementationOnce((_: unknown, cb: Function) => cb([{ documentId: "local-doc", frameId: 0, result: { status: "observed", origin: "http://127.0.0.1", url: "http://127.0.0.1/" } }])); + expect((await b.rpc("observeDocument", { ...params, expectedOrigin: "http://127.0.0.1", allowInsecureLoopback: true })).result.documentId).toBe("local-doc"); + expect(b.chrome.scripting.executeScript.mock.calls[0][0].args[3]).toBe(true); +}); + +it("binds private observation to an exact same-origin URL and returns it", async () => { + const b = await background(); + const owner = { session_id: "owner", turn_id: "turn", tabId: 1, expectedOrigin: "https://synthetic.invalid" }; + await b.rpc("createTab", owner); + const expectedUrl = "https://synthetic.invalid/login?flow=private#step"; + b.chrome.scripting.executeScript.mockImplementationOnce((_: unknown, cb: Function) => cb([{ documentId: "url-doc", frameId: 0, result: { status: "observed", origin: owner.expectedOrigin, url: expectedUrl } }])); + const response = await b.rpc("observeDocument", { ...owner, expectedUrl, selectors: ["#password"] }); + expect(response.error).toBeUndefined(); + expect(response.result).toMatchObject({ origin: owner.expectedOrigin, url: expectedUrl, documentId: "url-doc" }); + expect(b.chrome.scripting.executeScript.mock.calls[0][0].args[4]).toBe(expectedUrl); + + expect((await b.rpc("observeDocument", { ...owner, expectedUrl: "https://synthetic.invalid/wrong", selectors: ["#password"] })).error).toBeDefined(); + expect((await b.rpc("observeDocument", { ...owner, expectedUrl: "https://different.invalid/login", selectors: ["#password"] })).error).toBeDefined(); +}); + +it("cleans up a conflicting tab replacement without throwing or stealing ownership", async () => { + const b = await background(); + const owner = { session_id: "owner", turn_id: "turn" }; + const other = { session_id: "other", turn_id: "turn" }; + await b.rpc("createTab", owner); await b.rpc("createTab", other); + expect(() => b.chrome.tabs.onReplaced.emit(2, 1)).not.toThrow(); + expect((await b.rpc("attach", { ...owner, tabId: 2 })).error).toBeDefined(); + expect((await b.rpc("attach", { ...other, tabId: 2 })).result.attached).toBe(true); +}); + +it("enforces exact ownership for tab commands and filtered target enumeration", async () => { + const b = await background(); + const owner = { session_id: "owner", turn_id: "turn", tabId: 1 }; + await b.rpc("createTab", owner); + expect((await b.rpc("attach", { ...owner, session_id: "other" })).error).toBeDefined(); + expect((await b.rpc("executeCdp", { ...owner, method: "Target.getTargets" })).result.targetInfos).toEqual([{ tabId: 1 }]); + expect((await b.rpc("executeCdp", { ...owner, method: "Target.attachToTarget", commandParams: { targetId: "other" } })).error).toBeDefined(); +}); + +it("consumes private observations once and targets a specific Chrome document", async () => { + const b = await background(); + const owner = { session_id: "owner", turn_id: "turn", tabId: 1, expectedOrigin: "https://synthetic.invalid" }; + await b.rpc("createTab", owner); + await b.rpc("bindPage", owner); + const observation = (await b.rpc("observeDocument", { ...owner, selectors: ["#password"] })).result; + b.chrome.scripting.executeScript.mockImplementationOnce((_: unknown, cb: Function) => cb([{ documentId: "doc-1", frameId: 0, result: { status: "filled" } }])); + const params = { ...owner, ...observation, values: ["synthetic"] }; + expect((await b.rpc("privateFill", params)).result.status).toBe("filled"); + expect(b.chrome.scripting.executeScript.mock.calls[1][0]).toMatchObject({ target: { tabId: 1, documentIds: ["doc-1"] }, world: "ISOLATED" }); + expect((await b.rpc("privateFill", params)).result.status).toBe("refused"); + expect(b.chrome.scripting.executeScript).toHaveBeenCalledTimes(2); +}); + +it("invalidates private observations on navigation and disconnection", async () => { + const b = await background(); + const owner = { session_id: "owner", turn_id: "turn", tabId: 1, expectedOrigin: "https://synthetic.invalid" }; + await b.rpc("createTab", owner); + await b.rpc("bindPage", owner); + const observation = (await b.rpc("observeDocument", { ...owner, selectors: ["#password"] })).result; + b.chrome.tabs.onUpdated.emit(1, { status: "loading" }); + expect((await b.rpc("privateFill", { ...owner, ...observation, values: ["synthetic"] })).result.status).toBe("refused"); + expect(b.chrome.scripting.executeScript).toHaveBeenCalledTimes(1); + b.port.onMessage.emit({ jsonrpc: "2.0", method: "internal.releaseClient", params: { session_id: "owner" } }); + expect((await b.rpc("attach", owner)).error).toBeDefined(); +}); + +it("refuses private fill before an exact origin binding exists", async () => { + const b = await background(); + const owner = { session_id: "owner", turn_id: "turn", tabId: 1, expectedOrigin: "https://synthetic.invalid" }; + await b.rpc("createTab", owner); + const observation = (await b.rpc("observeDocument", { ...owner, selectors: ["#password"] })).result; + expect((await b.rpc("privateFill", { ...owner, ...observation, values: ["synthetic"] })).result.status).toBe("refused"); + expect(b.chrome.scripting.executeScript).toHaveBeenCalledTimes(1); +}); + +it("refuses an observation made before binding to a different origin", async () => { + const b = await background(); + const owner = { session_id: "owner", turn_id: "turn", tabId: 1, expectedOrigin: "https://synthetic.invalid" }; + await b.rpc("createTab", owner); + const observation = (await b.rpc("observeDocument", { ...owner, selectors: ["#password"] })).result; + await b.rpc("bindPage", { ...owner, expectedOrigin: "https://different.invalid" }); + expect((await b.rpc("privateFill", { ...owner, ...observation, values: ["synthetic"] })).result.status).toBe("refused"); + expect(b.chrome.scripting.executeScript).toHaveBeenCalledTimes(1); +}); + +it("serializes nested target.tabId private input behind a pending page observation", async () => { + const b = await background(); + const owner = { session_id: "owner", turn_id: "turn", tabId: 1, expectedOrigin: "https://synthetic.invalid" }; + await b.rpc("createTab", owner); await b.rpc("attach", owner); await b.rpc("bindPage", owner); + const observation = (await b.rpc("observeDocument", { ...owner, selectors: ["#password"] })).result; + b.chrome.scripting.executeScript.mockClear(); + let complete: Function | undefined; + b.chrome.scripting.executeScript.mockImplementationOnce((_: unknown, cb: Function) => { complete = cb; }); + const read = b.rpc("observePage", owner); + await vi.waitFor(() => expect(complete).toBeDefined()); + const fill = b.rpc("privateFill", { session_id: owner.session_id, turn_id: owner.turn_id, target: { tabId: 1 }, expectedOrigin: owner.expectedOrigin, ...observation, values: ["synthetic"] }); + await new Promise(resolve => setTimeout(resolve, 20)); + expect(b.chrome.scripting.executeScript).toHaveBeenCalledTimes(1); + complete!([{ documentId: "doc-1", frameId: 0, result: { status: "checked", allowed: true } }]); + await read; await fill; + expect(b.chrome.scripting.executeScript).toHaveBeenCalledTimes(3); +}); + +it("drops queued CDP actions after navigation rather than dispatching stale input", async () => { + const b = await background(); + const owner = { session_id: "owner", turn_id: "turn", tabId: 1 }; + await b.rpc("createTab", owner); await b.rpc("attach", owner); + let complete: Function | undefined; + b.chrome.debugger.sendCommand.mockImplementationOnce((_: unknown, __: unknown, ___: unknown, cb: Function) => { complete = cb; }); + const first = b.rpc("executeCdp", { ...owner, method: "Runtime.evaluate", commandParams: { expression: "1" } }); + await vi.waitFor(() => expect(complete).toBeDefined()); + const second = b.rpc("executeCdp", { ...owner, method: "Input.insertText", commandParams: { text: "synthetic" } }); + await new Promise(resolve => setTimeout(resolve, 10)); + b.chrome.tabs.onUpdated.emit(1, { status: "loading" }); complete!({}); + await first; + expect((await second).error).toBeDefined(); + expect(b.chrome.debugger.sendCommand).toHaveBeenCalledTimes(1); +}); + +it("never enumerates another session's tabs by omitting session_id", async () => { + const b = await background(); + await b.rpc("createTab", { session_id: "owner", turn_id: "turn" }); + expect((await b.rpc("getTabs")).error).toBeDefined(); +}); + +it("tags debugger events with the exact tab owner and drops unowned events", async () => { + const b = await background(); + await b.rpc("createTab", { session_id: "owner", turn_id: "turn" }); + b.chrome.debugger.onEvent.emit({ tabId: 1 }, "Runtime.consoleAPICalled", { value: "synthetic" }); + b.chrome.debugger.onEvent.emit({ tabId: 999 }, "Runtime.consoleAPICalled", {}); + const events = b.responses.filter(r => r.method === "onCDPEvent"); + expect(events).toHaveLength(1); + expect(events[0].params.session_id).toBe("owner"); +}); diff --git a/tests/security/client.test.ts b/tests/security/client.test.ts new file mode 100644 index 0000000..d4533f4 --- /dev/null +++ b/tests/security/client.test.ts @@ -0,0 +1,74 @@ +import { spawn } from "node:child_process"; +import fs from "node:fs"; +import net from "node:net"; +import path from "node:path"; +import { afterEach, expect, it } from "vitest"; +import { testTemp } from "../support/temp"; + +const cleanup: (() => void)[] = []; +afterEach(() => cleanup.splice(0).reverse().forEach(fn => fn())); + +function client(args: string[], endpoint?: string) { + const child = spawn(process.execPath, ["dist/native-host/client.js", ...args], { + env: { ...process.env, OPZERO_CHROME_HOST_SOCKET: endpoint }, stdio: ["pipe", "pipe", "pipe"] + }); + cleanup.push(() => child.kill()); + let stdout = ""; let stderr = ""; + child.stdout.on("data", chunk => { stdout += chunk; }); + child.stderr.on("data", chunk => { stderr += chunk; }); + const done = new Promise<{ code: number | null; stdout: string; stderr: string }>(resolve => child.on("close", code => resolve({ code, stdout, stderr }))); + return { child, done }; +} + +it("refuses argv payloads without printing them", async () => { + const c = client(["privateFill", '{"value":"synthetic-private-value"}']); + const result = await c.done; + expect(result.code).toBe(1); + expect(result.stdout + result.stderr).not.toContain("synthetic-private-value"); + expect(result.stderr).toContain("--stdio"); +}); + +it("frames streaming responses, suppresses events, and sanitizes private errors", async () => { + const directory = testTemp(); + const endpoint = path.join(directory, "s"); + const received: any[] = []; + const server = net.createServer(socket => { + cleanup.push(() => socket.destroy()); + socket.setEncoding("utf8"); + let text = ""; + socket.on("data", chunk => { + text += chunk; + let index; + while ((index = text.indexOf("\n")) >= 0) { + const message = JSON.parse(text.slice(0, index)); text = text.slice(index + 1); received.push(message); + socket.write('{"jsonrpc":"2.0","method":"onCDPEvent","params":{"value":"synthetic-private-value"}}\n'); + const response = `${JSON.stringify(message.method === "privateFill" + ? { jsonrpc: "2.0", id: message.id, error: { code: -1, message: "synthetic-private-value" } } + : { jsonrpc: "2.0", id: message.id, result: "pong" })}\n`; + socket.write(response.slice(0, 7)); + setImmediate(() => socket.write(response.slice(7))); + } + }); + }); + cleanup.push(() => { server.close(); fs.rmSync(directory, { recursive: true, force: true }); }); + await new Promise(resolve => server.listen(endpoint, resolve)); + const c = client(["--stdio"], endpoint); + c.child.stdin.end('{"jsonrpc":"2.0","id":1,"method":"privateFill","params":{"values":["synthetic-private-value"]}}\n'); + const result = await c.done; + expect(received).toHaveLength(1); + expect(result.stdout + result.stderr).not.toContain("synthetic-private-value"); + expect(JSON.parse(result.stdout).result).toEqual({ status: "not-filled-or-unknown", retry: false }); +}); + +it("never echoes malformed stdin payloads", async () => { + const directory = testTemp(); + const endpoint = path.join(directory, "s"); + const server = net.createServer(socket => cleanup.push(() => socket.destroy())); + cleanup.push(() => { server.close(); fs.rmSync(directory, { recursive: true, force: true }); }); + await new Promise(resolve => server.listen(endpoint, resolve)); + const c = client(["--stdio"], endpoint); + c.child.stdin.end('{synthetic-private-value\n'); + const result = await c.done; + expect(result.code).toBe(1); + expect(result.stdout + result.stderr).not.toContain("synthetic-private-value"); +}); diff --git a/tests/security/host.test.ts b/tests/security/host.test.ts new file mode 100644 index 0000000..c1461de --- /dev/null +++ b/tests/security/host.test.ts @@ -0,0 +1,214 @@ +import { spawn } from "node:child_process"; +import fs from "node:fs"; +import net from "node:net"; +import path from "node:path"; +import { afterEach, expect, it, vi } from "vitest"; +import { testTemp } from "../support/temp"; + +const cleanup: (() => void)[] = []; +afterEach(() => { cleanup.splice(0).reverse().forEach(fn => fn()); }); + +async function host(env: NodeJS.ProcessEnv = {}, protocolVersion = 2) { + const directory = testTemp(); + const endpoint = path.join(directory, "s"); + const child = spawn(process.execPath, ["dist/native-host/host.js"], { + env: { ...process.env, OPZERO_CHROME_HOST_SOCKET: endpoint, ...env }, stdio: ["pipe", "pipe", "pipe"] + }); + cleanup.push(() => { child.kill(); fs.rmSync(directory, { recursive: true, force: true }); }); + const native: any[] = []; + let buffer = Buffer.alloc(0); + child.stdout.on("data", chunk => { + buffer = Buffer.concat([buffer, chunk]); + while (buffer.length >= 4 && buffer.length >= buffer.readUInt32LE(0) + 4) { + const length = buffer.readUInt32LE(0); + const message = JSON.parse(buffer.subarray(4, length + 4).toString()); + if (typeof message.id === "string" && message.id.startsWith("protocol:")) { if (protocolVersion !== 0) send({ jsonrpc: "2.0", id: message.id, result: { protocolVersion } }); } + else native.push(message); + buffer = buffer.subarray(length + 4); + } + }); + function send(message: unknown) { + const body = Buffer.from(JSON.stringify(message)); + const header = Buffer.alloc(4); header.writeUInt32LE(body.length); + child.stdin.write(Buffer.concat([header, body])); + } + await vi.waitFor(() => expect(fs.existsSync(endpoint)).toBe(true)); + async function connect() { + const socket = net.connect(endpoint); + cleanup.push(() => socket.destroy()); + const messages: any[] = []; + let text = ""; + socket.setEncoding("utf8"); + socket.on("data", chunk => { + text += chunk; + let index; + while ((index = text.indexOf("\n")) >= 0) { + messages.push(JSON.parse(text.slice(0, index))); text = text.slice(index + 1); + } + }); + await new Promise((resolve, reject) => { socket.once("connect", resolve); socket.once("error", reject); }); + const request = (id: number, method: string, params: object = {}) => socket.write(`${JSON.stringify({ jsonrpc: "2.0", id, method, params })}\n`); + return { socket, messages, request }; + } + return { child, endpoint, native, send, connect }; +} + +it("replaces caller-asserted session IDs with per-connection authority", async () => { + const h = await host(); const a = await h.connect(); const b = await h.connect(); + a.request(1, "createTab", { session_id: "spoof", sessionId: "spoof", turn_id: "turn" }); + b.request(1, "createTab", { session_id: "spoof", turn_id: "turn" }); + await vi.waitFor(() => expect(h.native).toHaveLength(2)); + expect(h.native[0].params.session_id).not.toBe("spoof"); + expect(h.native[0].params.session_id).not.toBe(h.native[1].params.session_id); +}); + +it("refuses browser dispatch against a legacy extension with a distinct private status", async () => { + const h = await host({}, 1); const a = await h.connect(); + a.request(1, "privateFill", { values: ["synthetic"] }); + a.request(2, "createTab"); + await vi.waitFor(() => expect(a.messages).toHaveLength(2)); + expect(a.messages[0].result).toEqual({ status: "unsupported", retry: false }); + expect(a.messages[1].error.message).toContain("no action dispatched"); + expect(h.native).toHaveLength(0); +}); + +it("distinguishes pending protocol negotiation and suppresses pre-negotiation events", async () => { + const h = await host({}, 0); const a = await h.connect(); + a.request(1, "privateFill"); + await vi.waitFor(() => expect(a.messages[0]?.result?.status).toBe("not-ready")); + h.send({ jsonrpc: "2.0", method: "onControlStopped", params: {} }); + await new Promise(resolve => setTimeout(resolve, 30)); + expect(a.messages).toHaveLength(1); + expect(a.socket.destroyed).toBe(false); + expect(h.native).toHaveLength(0); +}); + +it("preserves large native results such as screenshots", async () => { + const h = await host(); const a = await h.connect(); + a.request(1, "executeCdp"); + await vi.waitFor(() => expect(h.native).toHaveLength(1)); + h.send({ jsonrpc: "2.0", id: h.native[0].id, result: { data: "x".repeat(2 * 1024 * 1024) } }); + await vi.waitFor(() => expect(a.messages[0]?.result?.data?.length).toBe(2 * 1024 * 1024)); +}); + +it("rejects a request whose injected authority would exceed Chrome's native frame limit", async () => { + const h = await host(); const a = await h.connect(); + const request = { jsonrpc: "2.0", id: 1, method: "executeCdp", params: { padding: "" } }; + request.params.padding = "x".repeat(1024 * 1024 - 32 - JSON.stringify(request).length); + a.socket.write(`${JSON.stringify(request)}\n`); + await vi.waitFor(() => expect(a.messages.length + h.native.length).toBeGreaterThan(0)); + expect(h.native).toHaveLength(0); + await vi.waitFor(() => expect(a.messages[0]?.error?.message).toContain("native frame limit")); + a.request(2, "host.ping"); + await vi.waitFor(() => expect(a.messages[1]?.result).toBe("pong")); +}); + +it("closes all client sessions when the extension stops control", async () => { + const h = await host(); const a = await h.connect(); const b = await h.connect(); + a.request(1, "host.info"); b.request(1, "host.info"); + await vi.waitFor(() => expect(a.messages.length + b.messages.length).toBe(2)); + expect(a.messages[0].result.extensionProtocol).toBe("ready"); + expect(b.messages[0].result.extensionProtocol).toBe("ready"); + h.send({ jsonrpc: "2.0", method: "onControlStopped", params: { reason: "synthetic stop" } }); + await vi.waitFor(() => expect(a.socket.destroyed && b.socket.destroyed).toBe(true)); + expect(a.messages[1].method).toBe("onControlStopped"); + expect(b.messages[1].method).toBe("onControlStopped"); +}); + +it("closes explicitly at the request limit instead of silently retaining a dead session", async () => { + const h = await host(); const a = await h.connect(); + for (let i = 1; i <= 10001; i++) a.request(i, "host.ping"); + await vi.waitFor(() => expect(a.messages).toHaveLength(10001)); + expect(a.messages.at(-1).error.message).toContain("Connection request limit reached"); + await vi.waitFor(() => expect(a.socket.destroyed).toBe(true)); +}); + +it("revokes disconnected clients and creates a fresh session on reconnect", async () => { + const h = await host(); const a = await h.connect(); + a.request(1, "host.info"); + await vi.waitFor(() => expect(a.messages).toHaveLength(1)); + const original = a.messages[0].result.session_id; + a.socket.destroy(); + await vi.waitFor(() => expect(h.native.some(message => message.method === "internal.releaseClient" && message.params.session_id === original)).toBe(true)); + const b = await h.connect(); b.request(1, "host.info"); + await vi.waitFor(() => expect(b.messages).toHaveLength(1)); + expect(b.messages[0].result.session_id).not.toBe(original); +}); + +it("revokes unknown outcomes on timeout without replaying requests", async () => { + const h = await host({ OPZERO_CHROME_REQUEST_TIMEOUT_MS: "50" }); const a = await h.connect(); + a.request(1, "createTab"); + await vi.waitFor(() => expect(a.messages).toHaveLength(1)); + expect(a.messages[0].error.message).toContain("Outcome unknown"); + expect(h.native.filter(message => message.method === "createTab")).toHaveLength(1); + await vi.waitFor(() => expect(h.native.some(message => message.method === "internal.releaseClient")).toBe(true)); +}); + +it("protects Unix endpoint permissions and never steals a running endpoint", async () => { + const h = await host(); + expect(fs.statSync(h.endpoint).mode & 0o777).toBe(0o600); + const child = spawn(process.execPath, ["dist/native-host/host.js"], { + env: { ...process.env, OPZERO_CHROME_HOST_SOCKET: h.endpoint }, stdio: ["pipe", "ignore", "pipe"] + }); + cleanup.push(() => child.kill()); + const code = await new Promise(resolve => child.on("exit", resolve)); + expect(code).toBe(1); + expect(fs.existsSync(h.endpoint)).toBe(true); + const a = await h.connect(); a.request(1, "host.ping"); + await vi.waitFor(() => expect(a.messages[0]?.result).toBe("pong")); +}); + +it.each(["dispatch", "release"])("removes its own endpoint when the native output pipe breaks during %s", async mode => { + const h = await host(); const a = await h.connect(); + a.request(1, "host.info"); + await vi.waitFor(() => expect(a.messages[0]?.result.extensionProtocol).toBe("ready")); + const exited = new Promise(resolve => h.child.once("exit", resolve)); + h.child.stdout.destroy(); + if (mode === "dispatch") a.request(2, "getInfo"); else a.socket.destroy(); + expect(await exited).toBe(1); + expect(fs.existsSync(h.endpoint)).toBe(false); + await vi.waitFor(() => expect(a.socket.destroyed).toBe(true)); + const replacement = net.createServer(socket => socket.end()); + await new Promise((resolve, reject) => { replacement.once("error", reject); replacement.listen(h.endpoint, resolve); }); + await new Promise(resolve => replacement.close(() => resolve())); +}); + +it("refuses reserved lifecycle calls from local clients and unscoped events", async () => { + const h = await host(); const a = await h.connect(); + a.request(1, "internal.releaseClient", { session_id: "someone-else" }); + await vi.waitFor(() => expect(a.messages[0]?.error).toBeDefined()); + expect(h.native).toHaveLength(0); + h.send({ jsonrpc: "2.0", method: "onCDPEvent", params: { source: { tabId: 1 } } }); + h.send({ jsonrpc: "2.0", method: "onDownloadChange", params: { url: "synthetic-private-value" } }); + await new Promise(resolve => setTimeout(resolve, 30)); + expect(a.messages).toHaveLength(1); +}); + +it("routes CDP events only to the owning connection, never by broadcast", async () => { + const h = await host(); const a = await h.connect(); const b = await h.connect(); + a.request(1, "createTab", { session_id: "owner-a", turn_id: "turn" }); + b.request(1, "createTab", { session_id: "owner-b", turn_id: "turn" }); + await vi.waitFor(() => expect(h.native).toHaveLength(2)); + h.send({ jsonrpc: "2.0", method: "onCDPEvent", params: { session_id: h.native[0].params.session_id, source: { tabId: 1 }, method: "Runtime.consoleAPICalled" } }); + await vi.waitFor(() => expect(a.messages).toHaveLength(1)); + await new Promise(resolve => setTimeout(resolve, 30)); + expect(b.messages).toHaveLength(0); +}); + +it("does not replay a duplicate action id", async () => { + const h = await host(); const a = await h.connect(); + a.request(1, "createTab", { session_id: "owner", turn_id: "turn" }); + a.request(1, "createTab", { session_id: "owner", turn_id: "turn" }); + await vi.waitFor(() => expect(h.native.length).toBeGreaterThan(0)); + await new Promise(resolve => setTimeout(resolve, 30)); + expect(h.native.filter(r => r.method === "createTab")).toHaveLength(1); +}); + +it("redacts private fill errors and results from the extension", async () => { + const h = await host(); const a = await h.connect(); + a.request(1, "privateFill", { values: ["synthetic-do-not-echo"] }); + await vi.waitFor(() => expect(h.native).toHaveLength(1)); + h.send({ jsonrpc: "2.0", id: h.native[0].id, error: { code: -1, message: "synthetic-do-not-echo" } }); + await vi.waitFor(() => expect(a.messages).toHaveLength(1)); + expect(JSON.stringify(a.messages)).not.toContain("synthetic-do-not-echo"); +}); diff --git a/tests/security/page-diagnostics.test.ts b/tests/security/page-diagnostics.test.ts new file mode 100644 index 0000000..bd8463e --- /dev/null +++ b/tests/security/page-diagnostics.test.ts @@ -0,0 +1,41 @@ +import { afterEach, expect, it, vi } from "vitest"; +import { pageControl } from "../../src/extension/page-control"; + +afterEach(() => vi.unstubAllGlobals()); + +it("allows embedded surfaces while preserving fixed private diagnostics", () => { + class Input { value = "synthetic-secret"; type = "password"; autocomplete = ""; } + let inputs: Input[] = [], embedded = false; + vi.stubGlobal("window", { top: undefined }); + window.top = window; + vi.stubGlobal("location", { origin: "https://synthetic.invalid" }); + vi.stubGlobal("HTMLInputElement", Input); + vi.stubGlobal("HTMLTextAreaElement", class {}); + vi.stubGlobal("document", { + querySelectorAll: (selector: string) => { + if (selector === "[") throw new Error("synthetic-secret"); + return inputs; + }, + querySelector: () => embedded ? {} : null, + }); + const check = (text = "[]") => pageControl("capture-check", location.origin, "", "", text); + expect(check()).toEqual({ status: "checked", allowed: true }); + embedded = true; + expect(check()).toEqual({ status: "checked", allowed: true }); + inputs = [new Input()]; + expect(check().reason).toBe("populated-private-input"); + inputs[0].type = "text"; + inputs[0].autocomplete = "one-time-code"; + expect(check().reason).toBe("populated-private-input"); + inputs[0].autocomplete = ""; + vi.stubGlobal("__opzeroPrivateNodes", new WeakSet(inputs)); + expect(check().reason).toBe("populated-private-input"); + vi.stubGlobal("__opzeroPrivateNodes", new WeakSet()); + expect(check('["#synthetic-private"]').reason).toBe("restored-private-selector"); + for (const malformed of ["{", "{}", "[1]", '["["]']) { + expect(check(malformed)).toEqual({ status: "checked", allowed: false, reason: "invalid-private-selectors" }); + } + vi.stubGlobal("__opzeroPrivateDocument", true); + expect(check().reason).toBe("private-quarantine"); + expect(check("{").reason).toBe("private-quarantine"); +}); diff --git a/tests/security/private-input.browser.test.ts b/tests/security/private-input.browser.test.ts new file mode 100644 index 0000000..f75d710 --- /dev/null +++ b/tests/security/private-input.browser.test.ts @@ -0,0 +1,159 @@ +import fs from "node:fs"; +import http from "node:http"; +import path from "node:path"; +import { chromium, type BrowserContext, type CDPSession, type Page } from "playwright-core"; +import { afterAll, beforeAll, describe, expect, it } from "vitest"; +import { background } from "../support/background"; +import { testTemp } from "../support/temp"; +import { pageControl } from "../../src/extension/page-control"; + +const executablePath = process.env.OPZERO_SYNTHETIC_CHROME; +describe.skipIf(!executablePath)("private guard in disposable headless Chrome (loopback only)", () => { + let browser: BrowserContext; + let page: Page; + let cdp: CDPSession; + let contextId: number; + let origin: string; + let directory: string; + let observePrivateFields: Function; + let fillPrivateFields: Function; + const server = http.createServer((_, response) => { + response.setHeader("Content-Type", "text/html"); + response.end('Synthetic only'); + }); + async function isolated() { + const tree = await cdp.send("Page.getFrameTree"); + contextId = (await cdp.send("Page.createIsolatedWorld", { frameId: tree.frameTree.frame.id, worldName: "private-test" })).executionContextId; + } + async function run(fn: Function, args: unknown[]) { + const result = await cdp.send("Runtime.callFunctionOn", { + executionContextId: contextId, functionDeclaration: fn.toString(), arguments: args.map(value => ({ value })), returnByValue: true + }); + if (result.exceptionDetails) throw new Error("Synthetic isolated script failed"); + return result.result.value; + } + async function reset() { await page.goto(origin); await isolated(); } + const observe = (token = "one-use", expectedUrl?: string) => run(observePrivateFields, [origin, ["#user", "#password"], token, true, expectedUrl]); + const fill = (expected = origin, token = "one-use") => run(fillPrivateFields, [expected, token, ["synthetic-user", "synthetic-private-value"]]); + + beforeAll(async () => { + const bundle = await background(); + const owner = { session_id: "bundle-test", turn_id: "turn", tabId: 1, expectedOrigin: "https://synthetic.invalid" }; + await bundle.rpc("createTab", owner); + await bundle.rpc("bindPage", owner); + const observed = (await bundle.rpc("observeDocument", { ...owner, selectors: ["#user", "#password"] })).result; + await bundle.rpc("privateFill", { ...owner, ...observed, values: ["synthetic-user", "synthetic-private-value"] }); + observePrivateFields = bundle.chrome.scripting.executeScript.mock.calls[0][0].func; + fillPrivateFields = bundle.chrome.scripting.executeScript.mock.calls[1][0].func; + await new Promise(resolve => server.listen(0, "127.0.0.1", resolve)); + const address = server.address(); + if (!address || typeof address === "string") throw new Error("No loopback listener"); + origin = `http://127.0.0.1:${address.port}`; + directory = testTemp("chrome-"); + browser = await chromium.launchPersistentContext(path.join(directory, "profile"), { + executablePath, headless: true, + env: { ...process.env, TMPDIR: directory }, + args: ["--disable-background-networking", "--disable-component-update", "--disable-sync", "--no-first-run", "--host-resolver-rules=MAP * ~NOTFOUND, EXCLUDE 127.0.0.1, EXCLUDE localhost"] + }); + await browser.route("**/*", route => new URL(route.request().url()).hostname === "127.0.0.1" ? route.continue() : route.abort()); + page = browser.pages()[0]; + cdp = await browser.newCDPSession(page); + await reset(); + }); + afterAll(async () => { + await browser?.close(); + await new Promise(resolve => server.close(() => resolve())); + if (directory) fs.rmSync(directory, { recursive: true, force: true }); + }); + + it("fills observed controls in an isolated world without returning values", async () => { + await reset(); + expect(await observe()).toEqual({ status: "observed", origin, url: `${origin}/` }); + expect(await fill()).toEqual({ status: "filled" }); + expect(await page.evaluate(() => (document.querySelector("#password") as HTMLInputElement).value === "synthetic-private-value")).toBe(true); + expect(await fill()).toEqual({ status: "refused" }); + }); + + it("rejects a public action after an input value changes without a DOM mutation", async () => { + await reset(); + const snapshot = await run(pageControl, ["observe", origin, "public-snapshot"]); + const button = snapshot.actions.find((action: { label: string }) => action.label === "Test"); + await page.evaluate(() => { (document.querySelector("#user") as HTMLInputElement).value = "changed-outside-driver"; }); + expect(await run(pageControl, ["act", origin, "public-snapshot", button.id])).toMatchObject({ status: "not-executed", reason: "stale" }); + }); + + it("atomically rejects a mismatching origin even with an otherwise valid observation", async () => { + await reset(); await observe(); + expect(await fill("https://not-this-origin.invalid")).toEqual({ status: "refused" }); + expect(await page.evaluate(() => (document.querySelector("#password") as HTMLInputElement).value.length)).toBe(0); + }); + + it("rejects a same-origin wrong expected path before any input", async () => { + await reset(); + expect(await observe("wrong-path", `${origin}/expected`)).toEqual({ status: "refused" }); + expect(await fill(origin, "wrong-path")).toEqual({ status: "refused" }); + expect(await page.evaluate(() => [...document.querySelectorAll("input")].every(input => input.value.length === 0))).toBe(true); + }); + + it("rejects same-document SPA drift before any input", async () => { + await reset(); await observe("spa-drift", `${origin}/`); + await page.evaluate(() => history.pushState(null, "", "/next")); + expect(await fill(origin, "spa-drift")).toEqual({ status: "refused" }); + expect(await page.evaluate(() => [...document.querySelectorAll("input")].every(input => input.value.length === 0))).toBe(true); + }); + + it("rejects replaced controls, stale documents, and wrong tokens without replay", async () => { + await reset(); await observe(); + await page.evaluate(() => { const input = document.querySelector("#password")!; input.replaceWith(input.cloneNode()); }); + expect(await fill()).toEqual({ status: "refused" }); + await reset(); await observe(); + expect(await fill(origin, "wrong")).toEqual({ status: "refused" }); + expect(await fill()).toEqual({ status: "refused" }); + await observe(); await page.reload(); await isolated(); + expect(await fill()).toEqual({ status: "refused" }); + }); + + it("does not continue into a field replaced by synchronous input handlers", async () => { + await reset(); await observe(); + await page.evaluate(() => document.querySelector("#user")!.addEventListener("input", () => { + const password = document.querySelector("#password")!; password.replaceWith(password.cloneNode()); + })); + expect(await fill()).toEqual({ status: "unknown" }); + expect(await page.evaluate(() => (document.querySelector("#password") as HTMLInputElement).value.length)).toBe(0); + expect(await fill()).toEqual({ status: "refused" }); + }); + + it("rejects duplicate, ambiguous, hidden, disabled, and non-input selectors", async () => { + await reset(); + for (const selectors of [["input"], ["button"], ["#user", "#user"], ["#missing"]]) { + expect(await run(observePrivateFields, [origin, selectors, "token", true])).toEqual({ status: "refused" }); + } + await page.evaluate(() => { (document.querySelector("#user") as HTMLInputElement).disabled = true; }); + expect(await observe()).toEqual({ status: "refused" }); + }); + + it("requires explicit loopback HTTP opt-in and rejects transparent or inert fields", async () => { + await reset(); + expect(await run(observePrivateFields, [origin, ["#user"], "token"])).toEqual({ status: "refused" }); + await page.evaluate(() => { (document.querySelector("#password") as HTMLElement).style.opacity = "0"; }); + expect(await observe()).toEqual({ status: "refused" }); + await reset(); + await page.evaluate(() => { document.body.inert = true; }); + expect(await observe()).toEqual({ status: "refused" }); + }); + + it("records bounded synthetic guard latency without a speedup claim", async () => { + await reset(); + const samples: number[] = []; + for (let i = 0; i < 30; i++) { + const start = performance.now(); + expect((await observe(`sample-${i}`)).status).toBe("observed"); + expect((await fill(origin, `sample-${i}`)).status).toBe("filled"); + samples.push(performance.now() - start); + } + samples.sort((a, b) => a - b); + const result = { surface: "two CDP calls: isolated observation + fill; loopback headless; not extension/native transport", n: samples.length, medianMs: samples[15], p95Ms: samples[28] }; + fs.writeFileSync("dist/synthetic-metrics.json", JSON.stringify(result, null, 2)); + expect(samples.every(Number.isFinite)).toBe(true); + }); +}); diff --git a/tests/security/scoped-observation.test.ts b/tests/security/scoped-observation.test.ts new file mode 100644 index 0000000..8e234ea --- /dev/null +++ b/tests/security/scoped-observation.test.ts @@ -0,0 +1,206 @@ +import { afterEach, beforeEach, expect, it, vi } from "vitest"; +import { pageControl } from "../../src/extension/page-control"; +import { parseObservation } from "../../src/shared/page-protocol"; +import { dom, Element, Input, Textarea, Button, Form, Slot, Text } from "../support/scoped-dom"; +import { background } from "../support/background"; + +let doc: ReturnType; +const origin = "https://synthetic.invalid"; +const observe = (controlsOnly = false, selectors: string[] = []) => pageControl("observe", origin, "fresh", "", null, { controlsOnly, selectors }); +const act = (id = "0", text?: string) => pageControl("act", origin, "fresh", id, text); +beforeEach(() => { doc = dom(); }); +afterEach(() => { for (const key of ["__opzeroSnapshot", "__opzeroSubmit", "__opzeroPrivateDocument", "__opzeroPrivateForms", "__opzeroPrivateNodes"]) Reflect.deleteProperty(globalThis, key); vi.unstubAllGlobals(); }); + +it("excludes embed fallback and closed hosts from text, labels, inventory and markers", () => { + const button = new Button("Public"); + for (const kind of ["iframe", "frame", "object", "embed", "x-closed"]) { + const opaque = new Element(kind, "OPAQUE_CANARY"); opaque.setAttribute("title", "OPAQUE_CANARY"); + opaque.append(new Button("OPAQUE_CANARY")); + if (kind === "x-closed") opaque.attachShadow("closed").append(new Button("OPAQUE_CANARY")); + button.append(opaque); + } + doc.body.append(button); + const raw = observe(); const result = parseObservation(raw); + expect(result.partial).toBe(true); expect(result.opaqueSurfaces).toHaveLength(5); + expect(result.actions).toHaveLength(1); expect(result.actions[0].label).toBe("Public"); + expect(JSON.stringify(raw)).not.toContain("CANARY"); + expect((globalThis as any).__opzeroSnapshot.targets[0].marker).not.toContain("CANARY"); + expect(act().status).toBe("executed"); expect(button.clicks).toBe(1); + expect(act().status).toBe("not-executed"); +}); + +it("fills public input next to embeds and tolerates unrelated text updates", () => { + const input = new Input(), status = new Element("p", "Loading"); + doc.body.append(input, status, new Element("iframe")); + expect(observe().status).toBe("observed"); status.append(new Text("Ready")); + expect(act("0", "Public value").status).toBe("executed"); expect(input.value).toBe("Public value"); +}); + +it.each(["closed", "object", "nested-open"])("keeps global light-root private checks outside the output scope: %s", surface => { + const host = new Element(surface === "object" ? "object" : "x-component"), input = new Input(); + input.type = "password"; input.value = "PRIVATE_CANARY"; host.append(input, new Text("OPAQUE_CANARY")); + if (surface === "closed") host.attachShadow("closed"); + if (surface === "nested-open") { input.remove(); const nested = new Element("x-nested"); host.attachShadow("open").append(nested); nested.attachShadow("open").append(input); } + doc.body.append(host, new Button()); + expect(observe().reason).toBe("populated-private-input"); + input.value = ""; const result = parseObservation(observe()); expect(result.actions).toHaveLength(1); + if (surface !== "nested-open") { expect(result.opaqueSurfaces).toHaveLength(1); expect(result.text).not.toContain("CANARY"); } +}); + +it("checks restored selectors in object fallback without including its public controls", () => { + const object = new Element("object"), input = new Input(); input.setAttribute("id", "restored"); input.value = "PRIVATE_CANARY"; object.append(input, new Button("OPAQUE_CANARY")); doc.body.append(object, new Button()); + expect(observe(false, ["#restored"]).reason).toBe("restored-private-selector"); + const result = parseObservation(observe()); expect(result.actions).toHaveLength(1); expect(JSON.stringify(result)).not.toContain("CANARY"); +}); + +it.each(["password", "otp", "weak", "restored"])("scans hidden inert open-root %s controls globally before output", mode => { + const host = new Element("x-open"), input = new Input(); + host.setAttribute("inert", ""); input.style.display = "none"; input.value = "PRIVATE_CANARY"; input.setAttribute("id", "private"); + if (mode === "password") input.type = "password"; + if (mode === "otp") input.autocomplete = "one-time-code"; + if (mode === "weak") vi.stubGlobal("__opzeroPrivateNodes", new WeakSet([input])); + host.attachShadow("open").append(input); doc.body.append(new Button(), new Element("iframe"), host); + const result = observe(false, mode === "restored" ? ["#private"] : []); + expect(result.reason).toBe(mode === "restored" ? "restored-private-selector" : "populated-private-input"); + expect(JSON.stringify(result)).not.toContain("CANARY"); expect((globalThis as any).__opzeroSnapshot).toBeUndefined(); +}); + +it("omits empty private controls and all associated labels, including aria references", () => { + const password = new Input(), label = new Element("label", "PRIVATE_LABEL_CANARY"), aria = new Element("span", "ARIA_CANARY"); + password.type = "password"; password.labels = [label]; aria.setAttribute("id", "private-name"); password.setAttribute("aria-labelledby", "private-name"); + doc.body.append(password, label, aria, new Button()); + expect(JSON.stringify(observe())).not.toContain("CANARY"); +}); + +it("does not recover a private label through a public aria-labelledby descendant", () => { + const password = new Input(), label = new Element("label"), nested = new Element("span", "PRIVATE_LABEL_CANARY"), button = new Button(""); + password.type = "password"; password.labels = [label]; nested.setAttribute("id", "nested"); label.append(nested); button.setAttribute("aria-labelledby", "nested"); + doc.body.append(password, label, button); expect(JSON.stringify(observe())).not.toContain("CANARY"); +}); + +it("traverses open roots and slots while enforcing composed inert ancestry", () => { + const host = new Element("x-open"), slot = new Slot(), button = new Button("Slotted"); + host.attachShadow("open").append(slot); host.append(button); button.assignedSlot = slot; slot.nodes = [button]; doc.body.append(host); + expect(parseObservation(observe()).actions[0].label).toBe("Slotted"); + slot.setAttribute("inert", ""); expect(act().status).toBe("not-executed"); + expect(parseObservation(observe()).actions).toEqual([]); +}); + +it.each(["replace", "label", "role", "private", "reparent", "root", "disabled", "modal", "value", "form"])("invalidates %s target changes and consumes the ref", change => { + const host = new Element("x-open"), input = new Input(); const root = host.attachShadow("open"); root.append(input); doc.body.append(host); + observe(); + if (change === "replace") { input.remove(); root.append(new Input()); } + if (change === "label") input.setAttribute("aria-label", "Changed"); + if (change === "role") input.setAttribute("role", "changed"); + if (change === "private") input.type = "password"; + if (change === "reparent") { const parent = new Element("div"); root.append(parent); parent.append(input); } + if (change === "root") doc.body.append(input); + if (change === "disabled") input.setAttribute("disabled", ""); + if (change === "modal") { const dialog = new Element("dialog"); dialog.modal = true; doc.body.append(dialog); } + if (change === "value") input.value = "Changed"; + if (change === "form") input.form = new Form(); + expect(act("0", "Test").status).toBe("not-executed"); expect(act("0", "Test").status).toBe("not-executed"); +}); + +it("reports composed ancestor aria-disabled state", () => { + const host = new Element("x-open"), button = new Button(); host.attachShadow("open").append(button); host.setAttribute("aria-disabled", "true"); doc.body.append(host); + expect(parseObservation(observe()).actions[0].disabled).toBe(true); expect(act().status).toBe("not-executed"); +}); + +it("rescans unrelated private inputs before dispatch and retains document quarantine", () => { + const button = new Button(), privateField = new Input(); privateField.type = "password"; doc.body.append(button, privateField); observe(); privateField.value = "PRIVATE_CANARY"; + expect(act().reason).toBe("populated-private-input"); expect(button.clicks).toBe(0); + privateField.value = ""; vi.stubGlobal("__opzeroPrivateDocument", true); expect(observe().reason).toBe("private-quarantine"); +}); + +it("keeps only the prepared single-use private submit exception", () => { + const form = new Form(), button = new Button(); button.type = "submit"; button.form = form; form.append(button); doc.body.append(form); + observe(); const prepared = pageControl("prepare-submit", origin, "fresh", "0"); + expect(prepared.status).toBe("prepared"); vi.stubGlobal("__opzeroPrivateDocument", true); vi.stubGlobal("__opzeroPrivateForms", new Set([form])); + expect(act().status).toBe("not-executed"); + expect(pageControl("submit", origin, prepared.submitToken!).status).toBe("executed"); + expect(pageControl("submit", origin, prepared.submitToken!).status).toBe("not-executed"); expect(button.clicks).toBe(1); +}); + +it("reports controls-only and all output bounds without treating labels as private-safe", () => { + doc.body.append(new Element("p", "BODY_CANARY " + "a".repeat(13000))); + for (let i = 0; i < 101; i++) doc.body.append(new Button("L".repeat(170)), new Element("iframe")); + const full = parseObservation(observe()); expect(full.truncation).toMatchObject({ text: true, labels: true, actions: true, opaqueSurfaces: true }); + const controls = parseObservation(observe(true)); expect(controls.text).toBe(""); expect(controls.mode).toBe("controls-only"); expect(controls.actions[0].label).toHaveLength(160); +}); + +it("reads only the scoped HTML head title and reports its truncation", () => { + doc.body.append(new Element("title", "SVG_TITLE_CANARY")); expect(parseObservation(observe()).title).toBe(""); + const head = new Element("head"); head.style.display = "none"; head.append(new Element("title", "T".repeat(210))); doc.append(head); + const result = parseObservation(observe()); expect(result.title).toHaveLength(200); expect(result.truncation.title).toBe(true); +}); + +it("fails closed without the documented closed-root detection primitive", () => { + vi.stubGlobal("chrome", {}); expect(observe().reason).toBe("unsupported-shadow-root"); +}); + +it.each(["origin", "document", "expiry", "token"])("binds freshness to %s", change => { + const button = new Button(); doc.body.append(button); observe(); + if (change === "origin") vi.stubGlobal("location", { origin: "https://other.invalid", href: "https://other.invalid/" }); + if (change === "document") vi.stubGlobal("document", dom()); + if (change === "expiry") (globalThis as any).__opzeroSnapshot.expires = 0; + expect(pageControl("act", origin, change === "token" ? "wrong" : "fresh", "0").status).toBe("not-executed"); + expect(button.clicks).toBe(0); +}); + +it("rechecks restored selectors before acting and refuses malformed selectors content-free", () => { + const button = new Button(), input = new Input(); input.setAttribute("id", "restored"); doc.body.append(button, input); observe(); input.value = "PRIVATE_CANARY"; + const result = pageControl("act", origin, "fresh", "0", null, { selectors: ["#restored"], controlsOnly: false }); + expect(result.reason).toBe("restored-private-selector"); expect(button.clicks).toBe(0); expect(JSON.stringify(result)).not.toContain("CANARY"); + expect(observe(false, ["["]).reason).toBe("invalid-private-selectors"); +}); + +it("serializes injection without runtime module dependencies", () => { + doc.body.append(new Button()); + const injected = new Function(`return (${pageControl.toString()})`)(); + expect(parseObservation(injected("observe", origin, "fresh")).actions[0].kind).toBe("click"); +}); + +it("executes the built background's serialized injection through observe and fill", async () => { + const b = await background(); + const owner = { session_id: "owner", turn_id: "turn", tabId: 1, expectedOrigin: origin }; + await b.rpc("createTab", owner); await b.rpc("bindPage", owner); + const input = new Input(); doc.body.append(input, new Element("iframe", "OPAQUE_CANARY")); + b.chrome.scripting.executeScript.mockImplementation((request: any, cb: Function) => { + const injected = new Function(`return (${request.func.toString()})`)(); + cb([{ documentId: "doc-1", frameId: 0, result: injected(...request.args) }]); + }); + const result = parseObservation((await b.rpc("observePage", owner)).result); + expect(result.partial).toBe(true); expect(JSON.stringify(result)).not.toContain("CANARY"); + expect((await b.rpc("actPage", { ...owner, snapshot: result.snapshot, actionId: "0", text: "Public" })).result.status).toBe("executed"); + expect(input.value).toBe("Public"); +}); + +it("returns unknown on post-dispatch failure and never replays", () => { + const button = new Button(); button.click = () => { button.clicks++; throw new Error("PRIVATE_CANARY"); }; doc.body.append(button); observe(); + expect(act()).toEqual({ status: "unknown", retry: false }); expect(act().status).toBe("not-executed"); expect(button.clicks).toBe(1); +}); + +it("supports bounded native number fill, rejects invalid text before mutation", () => { + const input = new Input(); input.type = "number"; doc.body.append(input); observe(); expect(act("0", "12.5").status).toBe("executed"); expect(input.value).toBe("12.5"); + observe(); expect(act("0", "abc").reason).toBe("invalid-fill"); expect(input.value).toBe("12.5"); +}); + +it("fills textarea through its native setter and refuses newly readonly controls", () => { + const textarea = new Textarea(); doc.body.append(textarea); observe(); + expect(act("0", "Public\ntext").status).toBe("executed"); expect(textarea.value).toBe("Public\ntext"); + observe(); textarea.readOnly = true; expect(act("0", "replacement").status).toBe("not-executed"); +}); + +it.each(["version", "partial", "kind", "kind-array", "extra", "flags", "mode", "disabled"])("validates v2 metadata: %s", change => { + doc.body.append(new Button(), new Element("iframe")); const raw: any = observe(); + if (change === "version") raw.pageProtocolVersion = 1; + if (change === "partial") raw.partial = false; + if (change === "kind") raw.opaqueSurfaces[0].kind = "CANARY"; + if (change === "kind-array") raw.opaqueSurfaces[0].kind = ["iframe"]; + if (change === "extra") raw.opaqueSurfaces[0].title = "CANARY"; + if (change === "flags") raw.truncation.text = "false"; + if (change === "mode") raw.mode = "controls-only"; + if (change === "disabled") raw.actions[0].disabled = 0; + expect(() => parseObservation(raw)).toThrow("Invalid page protocol"); +}); diff --git a/tests/security/tcp.test.ts b/tests/security/tcp.test.ts new file mode 100644 index 0000000..247d8a4 --- /dev/null +++ b/tests/security/tcp.test.ts @@ -0,0 +1,62 @@ +import { spawn } from "node:child_process"; +import { randomBytes } from "node:crypto"; +import fs from "node:fs"; +import net from "node:net"; +import path from "node:path"; +import { expect, it, vi } from "vitest"; +import { testTemp } from "../support/temp"; + +it("authenticates TCP from a private file and never forwards pre-auth disconnects", async () => { + const directory = testTemp(); + const tokenFile = path.join(directory, "token"); + const token = randomBytes(32).toString("hex"); + fs.writeFileSync(tokenFile, token, { mode: 0o600 }); + const reservation = net.createServer(); + await new Promise(resolve => reservation.listen(0, "127.0.0.1", resolve)); + const address = reservation.address(); + if (!address || typeof address === "string") throw new Error("No address"); + await new Promise(resolve => reservation.close(() => resolve())); + const env = { ...process.env, OPZERO_CHROME_HOST_TRANSPORT: "tcp", OPZERO_CHROME_HOST_PORT: String(address.port), OPZERO_CHROME_HOST_TOKEN_FILE: tokenFile }; + const host = spawn(process.execPath, ["dist/native-host/host.js"], { env, stdio: ["pipe", "pipe", "pipe"] }); + let buffer = Buffer.alloc(0); + const requests: any[] = []; + host.stdout.on("data", chunk => { + buffer = Buffer.concat([buffer, chunk]); + while (buffer.length >= 4 && buffer.length >= 4 + buffer.readUInt32LE(0)) { + const length = buffer.readUInt32LE(0); + const message = JSON.parse(buffer.subarray(4, length + 4).toString()); buffer = buffer.subarray(length + 4); + requests.push(message); + if (message.method === "getInfo") { + const body = Buffer.from(JSON.stringify({ jsonrpc: "2.0", id: message.id, result: { protocolVersion: 2 } })); + const header = Buffer.alloc(4); header.writeUInt32LE(body.length); host.stdin.write(Buffer.concat([header, body])); + } + } + }); + const sockets: net.Socket[] = []; + try { + await vi.waitFor(() => expect(requests).toHaveLength(1)); + const socket = await vi.waitFor(() => new Promise((resolve, reject) => { + const candidate = net.connect(address.port, "127.0.0.1"); + candidate.once("connect", () => resolve(candidate)); + candidate.once("error", reject); + })); + sockets.push(socket); + let denied = ""; + socket.on("data", chunk => { denied += chunk; }); + socket.write('{"jsonrpc":"2.0","id":1,"method":"createTab","params":{}}\n'); + await vi.waitFor(() => expect(denied).toContain("Authentication required")); + await vi.waitFor(() => expect(socket.destroyed).toBe(true)); + expect(requests).toHaveLength(1); + const client = spawn(process.execPath, ["dist/native-host/client.js", "host.info"], { env, stdio: ["ignore", "pipe", "pipe"] }); + let output = ""; let errors = ""; + client.stdout.on("data", chunk => { output += chunk; }); client.stderr.on("data", chunk => { errors += chunk; }); + const code = await new Promise(resolve => client.on("close", resolve)); + expect(code).toBe(0); + expect(JSON.parse(output).result.extensionProtocol).toBe("ready"); + expect(output + errors).not.toContain(token); + } finally { + sockets.forEach(socket => socket.destroy()); + host.kill(); + fs.rmSync(directory, { recursive: true, force: true }); + } +}); diff --git a/tests/security/transport.test.ts b/tests/security/transport.test.ts new file mode 100644 index 0000000..b402af6 --- /dev/null +++ b/tests/security/transport.test.ts @@ -0,0 +1,66 @@ +import fs from "node:fs"; +import net from "node:net"; +import path from "node:path"; +import { expect, it } from "vitest"; +import { ChromeTransport } from "../../src/native-host/transport"; +import { testTemp } from "../support/temp"; + +async function endpoint(observationError: string, check: (client: ChromeTransport, methods: string[]) => Promise, versions = { host: 2, transport: 2, page: 2 }) { + const root = testTemp(); + const socketPath = path.join(root, "s"); + const methods: string[] = []; + const sockets = new Set(); + let observations = 0; + const server = net.createServer(socket => { + sockets.add(socket); + let buffer = ""; + socket.setEncoding("utf8"); + socket.on("data", chunk => { + buffer += chunk; + for (let index; (index = buffer.indexOf("\n")) >= 0;) { + const request = JSON.parse(buffer.slice(0, index)); buffer = buffer.slice(index + 1); + methods.push(request.method); + const result = request.method === "host.info" ? { protocolVersion: versions.host, extensionProtocol: "ready", session_id: "test", epoch: "test" } + : request.method === "getInfo" ? { protocolVersion: versions.transport, pageProtocolVersion: versions.page } + : request.method === "createTab" ? { id: 1, active: false } + : request.method === "observePage" ? { status: "observed", pageProtocolVersion: 2, snapshot: "fresh", url: "https://synthetic.invalid/", title: "Ready", text: "Ready", actions: [], mode: "full", partial: false, opaqueSurfaces: [], truncation: { text: false, actions: false, opaqueSurfaces: false, labels: false, title: false } } + : {}; + const failed = request.method === "observePage" && observations++ === 0; + socket.write(JSON.stringify({ jsonrpc: "2.0", id: request.id, ...(failed ? { error: { code: -1, message: observationError } } : { result }) }) + "\n"); + } + }); + }); + let client: ChromeTransport | undefined; + try { + await new Promise(resolve => server.listen(socketPath, resolve)); + client = await ChromeTransport.connect(socketPath); + await check(client, methods); + } finally { + await client?.close(); + for (const socket of sockets) socket.destroy(); + await new Promise(resolve => server.close(() => resolve())); + fs.rmSync(root, { recursive: true, force: true }); + } +} + +it("waits through a removed main frame by repeating only observation", async () => { + await endpoint("Frame with ID 0 was removed.", async (client, methods) => { + const page = await client.open("https://synthetic.invalid/"); + expect((await client.waitFor(page, { text: "Ready" }, 500)).snapshot).toBe("fresh"); + expect(methods.filter(method => method === "observePage")).toHaveLength(2); + expect(methods.filter(method => method === "navigatePage")).toHaveLength(1); + expect(methods.filter(method => ["actPage", "privateFill", "submitPrivate"].includes(method))).toHaveLength(0); + }); +}); + +it.each(["Page owner revoked", "Transport closed; outcome unknown; no replay"])("does not hide %s while waiting", async error => { + await endpoint(error, async (client, methods) => { + const page = await client.open("https://synthetic.invalid/"); + await expect(client.waitFor(page, { text: "Ready" }, 500)).rejects.toThrow(error); + expect(methods.filter(method => method === "observePage")).toHaveLength(1); + }); +}); + +it.each(["host", "transport", "page"] as const)("rejects a mismatched %s protocol before page use", async key => { + await expect(endpoint("unused", async () => { throw new Error("unexpected connection"); }, { host: 2, transport: 2, page: 2, [key]: 1 })).rejects.toThrow(/protocol/); +}); diff --git a/tests/support/background.ts b/tests/support/background.ts new file mode 100644 index 0000000..f3f2785 --- /dev/null +++ b/tests/support/background.ts @@ -0,0 +1,45 @@ +import fs from "node:fs"; +import vm from "node:vm"; +import { webcrypto } from "node:crypto"; +import { expect, vi } from "vitest"; + +function event() { + const listeners: ((...args: any[]) => void)[] = []; + return { addListener: (fn: (...args: any[]) => void) => listeners.push(fn), emit: (...args: any[]) => listeners.forEach(fn => fn(...args)) }; +} + +export async function background() { + const responses: any[] = []; + const tabs = new Map(); + let nextTab = 1; + const port = { onMessage: event(), onDisconnect: event(), postMessage: (msg: any) => responses.push(msg), disconnect: vi.fn() }; + const storage: Record = {}; + const chrome = { + runtime: { connectNative: () => port, getManifest: () => ({ version: "test" }), id: "test", onMessage: event(), onStartup: event(), onInstalled: event(), onUpdateAvailable: event(), reload: vi.fn() }, + storage: { local: { get: (key: string, cb: Function) => cb({ [key]: storage[key] }), set: (value: object, cb: Function) => { Object.assign(storage, value); cb(); } }, session: { get: (_: unknown, cb: Function) => cb({}) } }, + alarms: { create: vi.fn(), clear: vi.fn(), onAlarm: event() }, + windows: { getCurrent: (_: unknown, cb: Function) => cb({ id: 1, type: "normal" }) }, + tabs: { + create: vi.fn((props: any, cb: Function) => { const tab = { id: nextTab++, ...props }; tabs.set(tab.id, tab); cb(tab); }), + get: (id: number, cb: Function) => cb(tabs.get(id)), + group: (_: unknown, cb: Function) => cb(1), ungroup: (_: unknown, cb: Function) => cb(), + query: (_: unknown, cb: Function) => cb([...tabs.values()]), + onRemoved: event(), onReplaced: event(), onUpdated: event() + }, + history: { search: vi.fn((_: unknown, cb: Function) => cb([])) }, + tabGroups: { get: (_: unknown, cb: Function) => cb({}), update: (_: unknown, __: unknown, cb: Function) => cb({}) }, + debugger: { attach: vi.fn((_: unknown, __: unknown, cb: Function) => cb()), detach: (_: unknown, cb: Function) => cb(), getTargets: (cb: Function) => cb([{ tabId: 1 }, { tabId: 2 }]), sendCommand: vi.fn((_: unknown, __: unknown, ___: unknown, cb: Function) => cb({})), onEvent: event(), onDetach: event() }, + downloads: { onCreated: event(), onChanged: event() }, + scripting: { executeScript: vi.fn((_: unknown, cb: Function) => cb([{ documentId: "doc-1", frameId: 0, result: { status: "observed", origin: "https://synthetic.invalid", url: "https://synthetic.invalid/" } }])) } + }; + vm.runInNewContext(fs.readFileSync("dist/extension/background.js", "utf8"), { chrome, crypto: webcrypto, setTimeout, clearTimeout, console, URL, TextEncoder, TextDecoder, AbortController }); + await new Promise(resolve => setTimeout(resolve, 10)); + let id = 0; + async function rpc(method: string, params: Record = {}) { + const requestId = ++id; + port.onMessage.emit({ jsonrpc: "2.0", id: requestId, method, params }); + await vi.waitFor(() => expect(responses.find(r => r.id === requestId)).toBeDefined()); + return responses.find(r => r.id === requestId); + } + return { chrome, port, responses, rpc }; +} diff --git a/tests/support/scoped-dom.ts b/tests/support/scoped-dom.ts new file mode 100644 index 0000000..9f94523 --- /dev/null +++ b/tests/support/scoped-dom.ts @@ -0,0 +1,83 @@ +import { vi } from "vitest"; + +export class Node { + static TEXT_NODE = 3; + nodeType = 1; + nodeValue: string | null = null; + childNodes: Node[] = []; + parentNode: Node | null = null; + get parentElement(): Element | null { return this.parentNode instanceof Element ? this.parentNode : null; } + get ownerDocument(): Document { return this.getRootNode() instanceof ShadowRoot ? (this.getRootNode() as ShadowRoot).host.ownerDocument : this.getRootNode() as Document; } + get isConnected(): boolean { return this.getRootNode() instanceof Document || this.getRootNode() instanceof ShadowRoot && (this.getRootNode() as ShadowRoot).host.isConnected; } + getRootNode(): Node { return this.parentNode?.getRootNode() ?? this; } + append(...nodes: Node[]) { for (const node of nodes) { node.remove(); this.childNodes.push(node); node.parentNode = this; } } + remove() { if (this.parentNode) this.parentNode.childNodes = this.parentNode.childNodes.filter(n => n !== this); this.parentNode = null; } + querySelectorAll(selector: string): Element[] { + if (selector === "[") throw new Error("INVALID_SELECTOR_CANARY"); + return this.childNodes.flatMap(n => [...(n instanceof Element && n.matches(selector) ? [n] : []), ...n.querySelectorAll(selector)]); + } + querySelector(selector: string) { return this.querySelectorAll(selector)[0] ?? null; } + getElementById(id: string) { return this.querySelector(`#${id}`); } +} +export class Text extends Node { + nodeType = 3; + assignedSlot: Slot | null = null; + constructor(value: string) { super(); this.nodeValue = value; } +} +export class Element extends Node { + attrs = new Map(); + assignedSlot: Slot | null = null; + root: ShadowRoot | null = null; + style = { display: "block", visibility: "visible", opacity: "1" }; + clicks = 0; + modal = false; + constructor(public localName: string, text = "") { super(); if (text) this.append(new Text(text)); } + get shadowRoot() { return this.root?.mode === "open" ? this.root : null; } + attachShadow(mode: "open" | "closed") { return this.root = new ShadowRoot(this, mode); } + getAttribute(name: string) { return this.attrs.get(name) ?? null; } + setAttribute(name: string, value: string) { this.attrs.set(name, value); } + hasAttribute(name: string) { return this.attrs.has(name); } + checkVisibility() { return this.style.display !== "none"; } + matches(selector: string): boolean { return selector.split(",").some(s => s === ":modal" ? this.modal : s === ":disabled" ? this.hasAttribute("disabled") : s.startsWith("#") ? this.getAttribute("id") === s.slice(1) : ["[role=button]", "[role=option]", "[role=menuitem]", "[role=menuitemcheckbox]", "[role=menuitemradio]"].includes(s) ? this.getAttribute("role") === s.slice(6, -1) : s === "a[href]" ? this.localName === "a" && this.hasAttribute("href") : s === "head > title" ? this.localName === "title" && this.parentElement?.localName === "head" : this.localName === s); } + click() { this.clicks++; } + dispatchEvent(_event: Event) { return true; } +} +export class Input extends Element { + constructor() { super("input"); } + private content = ""; + get value() { return this.content; } + set value(value: string) { this.content = value; } + get type() { return this.getAttribute("type") ?? "text"; } + set type(value: string) { this.setAttribute("type", value); } + autocomplete = ""; + readOnly = false; + labels: Element[] = []; + form: Form | null = null; + formAction = ""; + formTarget = ""; +} +export class Textarea extends Element { + constructor() { super("textarea"); } + private content = ""; + get value() { return this.content; } + set value(value: string) { this.content = value; } + readOnly = false; + labels: Element[] = []; + form: Form | null = null; +} +export class Button extends Element { type = "button"; form: Form | null = null; formAction = ""; formTarget = ""; constructor(text = "Continue") { super("button", text); } } +export class Anchor extends Element { href = "https://synthetic.invalid/"; target = ""; } +export class Form extends Element { action = "https://synthetic.invalid/"; target = ""; method = "post"; constructor() { super("form"); } } +export class Slot extends Element { nodes: Node[] = []; constructor() { super("slot"); } assignedNodes() { return this.nodes; } } +export class ShadowRoot extends Node { constructor(public host: Element, public mode: "open" | "closed") { super(); } } +export class Document extends Node { body = new Element("body"); constructor() { super(); this.append(this.body); } } + +export function dom() { + const document = new Document(); + const win: Record = {}; win.top = win; + for (const [name, value] of Object.entries({ Node, Text, Element, HTMLElement: Element, HTMLInputElement: Input, + HTMLTextAreaElement: Textarea, HTMLButtonElement: Button, HTMLAnchorElement: Anchor, HTMLSlotElement: Slot, + ShadowRoot, Document, document, window: win, location: { origin: "https://synthetic.invalid", href: "https://synthetic.invalid/" }, + getComputedStyle: (e: Element) => e.style, chrome: { dom: { openOrClosedShadowRoot: (e: Element) => e.root } } })) vi.stubGlobal(name, value); + return document; +} diff --git a/tests/support/temp.ts b/tests/support/temp.ts new file mode 100644 index 0000000..1106d26 --- /dev/null +++ b/tests/support/temp.ts @@ -0,0 +1,9 @@ +import fs from "node:fs"; +import os from "node:os"; +import path from "node:path"; + +export function testTemp(prefix = "oc-") { + const root = process.env.OPZERO_TEST_TMPDIR || path.join(os.tmpdir(), "opencode"); + fs.mkdirSync(root, { recursive: true, mode: 0o700 }); + return fs.mkdtempSync(path.join(root, prefix)); +} diff --git a/vite.node.config.ts b/vite.node.config.ts index 4f7036a..71fff2d 100644 --- a/vite.node.config.ts +++ b/vite.node.config.ts @@ -4,6 +4,7 @@ import { defineConfig } from "vite"; const nodeEntries = { "native-host/host": resolve(__dirname, "src/native-host/host.ts"), "native-host/client": resolve(__dirname, "src/native-host/client.ts"), + "native-host/transport": resolve(__dirname, "src/native-host/transport.ts"), "scripts/install-native-host": resolve(__dirname, "src/scripts/install-native-host.ts"), "scripts/check-extension-installed": resolve(__dirname, "src/scripts/check-extension-installed.ts"), "scripts/check-native-host-manifest": resolve(__dirname, "src/scripts/check-native-host-manifest.ts"), From edebaee73ec08a87d21e11a8ced1ecb7d544f79d Mon Sep 17 00:00:00 2001 From: afif Date: Mon, 28 Sep 2026 13:41:44 +0800 Subject: [PATCH 02/17] Stop tracking a machine-local node_modules symlink --- node_modules | 1 - 1 file changed, 1 deletion(-) delete mode 120000 node_modules diff --git a/node_modules b/node_modules deleted file mode 120000 index 4062027..0000000 --- a/node_modules +++ /dev/null @@ -1 +0,0 @@ -/Users/afif.alauddin/.config/opencode/local/preview-private-input-20260922/op-chrome/node_modules \ No newline at end of file From 4df62a5e3dee7af2628e068e4f1b49e9af78d849 Mon Sep 17 00:00:00 2001 From: afif Date: Mon, 28 Sep 2026 13:43:21 +0800 Subject: [PATCH 03/17] Add PDF upload and native fallback controls from the installed 0.2.0 build These edits were uncommitted in the archived afif/native-fallback-controls worktree. Built together with 4e734a0 they reproduce the installed 0.2.0 extension byte for byte. --- .../chrome-control/native-host/transport.js | 34 ++- src/extension/background.ts | 48 +++- src/extension/page-control.ts | 154 +++++++++-- src/native-host/transport.ts | 13 + src/shared/page-protocol.ts | 4 +- tests/security/background.test.ts | 67 +++++ tests/security/page-diagnostics.test.ts | 3 + tests/security/private-input.browser.test.ts | 255 +++++++++++++++++- tests/security/scoped-observation.test.ts | 200 +++++++++++++- tests/support/background.ts | 6 +- tests/support/scoped-dom.ts | 18 +- 11 files changed, 757 insertions(+), 45 deletions(-) diff --git a/skills/chrome-control/native-host/transport.js b/skills/chrome-control/native-host/transport.js index c00bfea..439df1b 100644 --- a/skills/chrome-control/native-host/transport.js +++ b/skills/chrome-control/native-host/transport.js @@ -22,7 +22,7 @@ function parseObservation(value) { if (raw.status !== "observed" || raw.pageProtocolVersion !== 2 || raw.mode !== "full" && raw.mode !== "controls-only" || !Array.isArray(raw.actions) || raw.actions.length > 100 || !Array.isArray(raw.opaqueSurfaces) || raw.opaqueSurfaces.length > 100) fail(); const actions = raw.actions.map((value) => { const a = object(value); - if (a.kind !== "fill" && a.kind !== "click") return fail(); + if (a.kind !== "fill" && a.kind !== "click" && a.kind !== "upload") return fail(); return { id: string(a.id, 100), kind: a.kind, @@ -244,6 +244,38 @@ var ChromeTransport = class ChromeTransport { ...typeof result.reason === "string" ? { reason: result.reason } : {} }; } + async uploadFile(page, snapshot, actionId, filePath) { + if (this.#recordings.has(page.tabId) || !node_path.default.isAbsolute(filePath) || filePath.includes("\0") || node_path.default.extname(filePath).toLowerCase() !== ".pdf") throw new Error("Valid local PDF required and recording must be stopped"); + const info = await node_fs_promises.default.lstat(filePath); + if (!info.isFile() || info.isSymbolicLink() || info.uid !== process.getuid?.() || info.size < 1) throw new Error("Current-user-owned regular PDF required"); + const handle = await node_fs_promises.default.open(filePath, "r"); + try { + const magic = Buffer.alloc(5); + if ((await handle.read(magic, 0, 5, 0)).bytesRead !== 5 || magic.toString() !== "%PDF-") throw new Error("PDF signature required"); + } finally { + await handle.close(); + } + const name = node_path.default.basename(filePath); + const result = object(await this.#call("uploadFile", { + ...this.#owned(page), + snapshot, + actionId, + path: filePath, + name, + size: info.size + })); + if (result.status !== "attached" && result.status !== "not-executed" && result.status !== "unknown") throw new Error("Invalid upload result; no replay"); + return result.status === "attached" ? { + status: "attached", + retry: false, + name, + mime: "application/pdf", + size: info.size + } : { + status: result.status, + retry: false + }; + } async privateFill(page, selectors, values, options = {}) { if (this.#recordings.has(page.tabId)) throw new Error("Stop recording before private input"); const params = this.#owned(page); diff --git a/src/extension/background.ts b/src/extension/background.ts index 90b19ed..717bde2 100644 --- a/src/extension/background.ts +++ b/src/extension/background.ts @@ -584,7 +584,7 @@ class NativeTransport { } } -function injectPage(tabId: number, origin: string, operation: "observe" | "act" | "capture-check" | "document-check" | "prepare-submit" | "submit", token = "", actionId = "", text?: string, documentId?: string, options: PageOptions = { selectors: [], controlsOnly: false }) { +function injectPage(tabId: number, origin: string, operation: "observe" | "act" | "capture-check" | "document-check" | "prepare-submit" | "submit" | "prepare-file" | "validate-file" | "verify-file" | "cancel-file", token = "", actionId = "", text?: string, documentId?: string, options: PageOptions = { selectors: [], controlsOnly: false }) { return Effect.gen(function* () { const chromeApi = yield* ChromeApi; const owner = getSessionForTab(tabId); @@ -641,6 +641,40 @@ function assertCapture(tabId: number, origin: string) { } const api: Record = { + uploadFile: (params: JsonRecord = {}) => Effect.gen(function* () { + const { tabId } = yield* Effect.try({ try: () => requireSessionTab(params), catch: toError }); + const origin = pageOrigins.get(tabId), snapshot = pageSnapshots.get(tabId); + pageSnapshots.delete(tabId); + const valid = typeof params.snapshot === "string" && typeof params.actionId === "string" && typeof params.path === "string" && params.path.startsWith("/") && !params.path.includes("\0") + && typeof params.name === "string" && params.name.length > 0 && params.name.length <= 255 && !/[\\/\0]/.test(params.name) + && Number.isInteger(params.size) && (params.size as number) > 0; + if (!origin || !snapshot || params.snapshot !== snapshot.token || !valid || pageRecordings.has(tabId)) return { status: "not-executed", retry: false }; + const context = yield* Effect.either(scopedContext(tabId, origin, snapshot.documentId)); + if (Either.isLeft(context)) return { status: "not-executed", retry: false }; + const prepared = yield* Effect.either(injectPage(tabId, origin, "prepare-file", snapshot.token, params.actionId as string, undefined, + context.right.documentId, { selectors: context.right.selectors, controlsOnly: false })); + if (Either.isLeft(prepared) || prepared.right.result?.status !== "prepared" || typeof prepared.right.result.fileToken !== "string" + || typeof prepared.right.result.selector !== "string") return { status: "not-executed", retry: false }; + const fileToken = prepared.right.result.fileToken, selector = prepared.right.result.selector; + const cancel = () => injectPage(tabId, origin, "cancel-file", fileToken, "", undefined, context.right.documentId); + const root = yield* Effect.either(sendDebuggerCommand<{ root?: { nodeId?: number } }>(tabId, "DOM.getDocument", { depth: 0, pierce: true })); + if (Either.isLeft(root) || !Number.isInteger(root.right.root?.nodeId)) { yield* Effect.either(cancel()); return { status: "not-executed", retry: false }; } + const matches = yield* Effect.either(sendDebuggerCommand<{ nodeIds?: number[] }>(tabId, "DOM.querySelectorAll", { nodeId: root.right.root!.nodeId, selector })); + if (Either.isLeft(matches) || !Array.isArray(matches.right.nodeIds) || matches.right.nodeIds.length !== 1 || !Number.isInteger(matches.right.nodeIds[0])) { + yield* Effect.either(cancel()); return { status: "not-executed", retry: false }; + } + const fresh = yield* Effect.either(scopedContext(tabId, origin, context.right.documentId)); + if (Either.isLeft(fresh)) { yield* Effect.either(cancel()); return { status: "not-executed", retry: false }; } + const validated = yield* Effect.either(injectPage(tabId, origin, "validate-file", fileToken, "", undefined, context.right.documentId, + { selectors: fresh.right.selectors, controlsOnly: false })); + if (Either.isLeft(validated) || validated.right.result?.status !== "validated") { yield* Effect.either(cancel()); return { status: "not-executed", retry: false }; } + const dispatched = yield* Effect.either(sendDebuggerCommand(tabId, "DOM.setFileInputFiles", { nodeId: matches.right.nodeIds[0], files: [params.path] })); + if (Either.isLeft(dispatched)) { yield* Effect.either(cancel()); return { status: "unknown", retry: false }; } + const verified = yield* Effect.either(injectPage(tabId, origin, "verify-file", fileToken, "", JSON.stringify({ name: params.name, size: params.size }), context.right.documentId, + { selectors: fresh.right.selectors, controlsOnly: false })); + if (Either.isLeft(verified) || verified.right.result?.status !== "attached") { yield* Effect.either(cancel()); return { status: "unknown", retry: false }; } + return { status: "attached", retry: false }; + }), preparePrivateSubmit: (params: JsonRecord = {}) => Effect.gen(function* () { const { tabId } = yield* Effect.try({ try: () => requireSessionTab(params), catch: toError }); const origin = pageOrigins.get(tabId), snapshot = pageSnapshots.get(tabId); @@ -851,12 +885,16 @@ const api: Record = { const chromeApi = yield* ChromeApi; const session = yield* Effect.try({ try: () => ensureSession(params), catch: toError }); const name = String(params.name || DEFAULT_SESSION_TITLE).slice(0, 80); - session.title = name || DEFAULT_SESSION_TITLE; + const nextTitle = name || DEFAULT_SESSION_TITLE; + const confirmed = session.groupId != null; if (session.groupId != null) { - yield* chromeApi.safeCall("tabGroups", "update", session.groupId, { title: session.title, collapsed: false }); + yield* chromeApi.call("tabGroups", "update", session.groupId, { title: nextTitle, collapsed: false }); + const group = yield* chromeApi.call("tabGroups", "get", session.groupId); + if (group.title !== nextTitle) throw createRpcError("Session group title confirmation failed"); } + session.title = nextTitle; yield* persistGroupState(); - return { name: session.title }; + return { name: session.title, confirmed }; }), attach: (params: JsonRecord = {}) => Effect.gen(function* () { @@ -1049,7 +1087,7 @@ async function handleJsonRpcRequest(message: JsonRpcRequest, respond: (response: const rpcParams = typeof params === "object" && params != null ? params as JsonRecord : {}; const targetParams = typeof rpcParams.target === "object" && rpcParams.target != null ? rpcParams.target as JsonRecord : {}; const tabId = Number(rpcParams.tabId ?? targetParams.tabId); - const serialized = ["preparePrivateSubmit", "submitPrivate", "recordingState", "bindPage", "navigatePage", "observePage", "actPage", "capturePage", "observeDocument", "privateFill", "executeCdp"].includes(method) && Number.isInteger(tabId); + const serialized = ["uploadFile", "preparePrivateSubmit", "submitPrivate", "recordingState", "bindPage", "navigatePage", "observePage", "actPage", "capturePage", "observeDocument", "privateFill", "executeCdp"].includes(method) && Number.isInteger(tabId); let result: unknown; if (serialized) { const previous = pageOperations.get(tabId) || Promise.resolve(); diff --git a/src/extension/page-control.ts b/src/extension/page-control.ts index 40022fb..1ee6178 100644 --- a/src/extension/page-control.ts +++ b/src/extension/page-control.ts @@ -2,27 +2,41 @@ import type { PageAction } from "../shared/page-protocol"; export type { PageAction } from "../shared/page-protocol"; type Target = { node: Element; root: Node; form: HTMLFormElement | null; ancestry: Node[]; marker: string; action: PageAction }; type Snapshot = { token: string; origin: string; document: Document; targets: Target[]; expires: number }; -type World = typeof globalThis & { __opzeroSnapshot?: Snapshot; __opzeroPrivateNodes?: WeakSet; __opzeroPrivateDocument?: boolean; __opzeroPrivateForms?: Set; __opzeroSubmit?: { token: string; target: Target; node: HTMLButtonElement | HTMLInputElement; form: HTMLFormElement; expires: number } }; +type PreparedFile = { token: string; target: Target; node: HTMLInputElement; attribute: string; expires: number; changes: number; listener: EventListener }; +type World = typeof globalThis & { __opzeroSnapshot?: Snapshot; __opzeroPrivateNodes?: WeakSet; __opzeroPrivateDocument?: boolean; __opzeroPrivateForms?: Set; __opzeroSubmit?: { token: string; target: Target; node: HTMLButtonElement | HTMLInputElement; form: HTMLFormElement; expires: number }; __opzeroFile?: PreparedFile }; export type PageOptions = { selectors: string[]; controlsOnly: boolean }; // executeScript serializes this function without module bindings. -export function pageControl(operation: "observe" | "act" | "capture-check" | "document-check" | "prepare-submit" | "submit", expectedOrigin: string, token: string, actionId = "", text?: string | null, options: PageOptions = { selectors: [], controlsOnly: false }) { +export function pageControl(operation: "observe" | "act" | "capture-check" | "document-check" | "prepare-submit" | "submit" | "prepare-file" | "validate-file" | "verify-file" | "cancel-file", expectedOrigin: string, token: string, actionId = "", text?: string | null, options: PageOptions = { selectors: [], controlsOnly: false }) { const world = globalThis as World; const visible = (e: Element) => e.checkVisibility({ checkOpacity: true, checkVisibilityCSS: true }); const privateInput = (e: Element) => world.__opzeroPrivateNodes?.has(e) || (e instanceof HTMLInputElement && (e.type === "password" || e.autocomplete === "one-time-code")); - const populated = () => [...document.querySelectorAll("input,textarea")].some(e => (e instanceof HTMLInputElement || e instanceof HTMLTextAreaElement) && e.value && privateInput(e)); + const displayMask = (e: Element) => e instanceof HTMLInputElement && e.type === "password" && e.disabled + && !world.__opzeroPrivateNodes?.has(e) && e.autocomplete !== "one-time-code" && e.value === e.defaultValue && /^•{4,64}$/.test(e.value); + const populatedPrivate = (e: HTMLInputElement | HTMLTextAreaElement) => !!e.value && privateInput(e) && !displayMask(e); const snapshot = world.__opzeroSnapshot; - if (operation !== "capture-check" && operation !== "document-check") delete world.__opzeroSnapshot; + if (!["capture-check", "document-check", "validate-file", "verify-file", "cancel-file"].includes(operation)) delete world.__opzeroSnapshot; const refused = (reason: string) => ({ status: operation === "observe" ? "refused" : "not-executed", reason, retry: false }); if (location.origin !== expectedOrigin || window !== window.top) return refused("origin"); if (operation === "document-check") return { status: "checked" }; if (operation === "capture-check") { if (world.__opzeroPrivateDocument) return { status: "checked", allowed: false, reason: "private-quarantine" }; + if (typeof chrome === "undefined" || typeof chrome.dom?.openOrClosedShadowRoot !== "function") return { status: "checked", allowed: false, reason: "unsupported-shadow-root" }; + // Screenshots render every shadow root, including roots that observation treats as opaque. + const captureRoots: (Document | ShadowRoot)[] = [document]; + const collect = (node: Node) => { + const shadow = node instanceof HTMLElement ? chrome.dom.openOrClosedShadowRoot(node) : null; + if (shadow) { captureRoots.push(shadow); collect(shadow); } + for (const child of node.childNodes) collect(child); + }; + try { collect(document); } catch { return { status: "checked", allowed: false, reason: "unsupported-shadow-root" }; } + const fields = (selector: string) => captureRoots.flatMap(root => [...root.querySelectorAll(selector)]) + .filter((e): e is HTMLInputElement | HTMLTextAreaElement => e instanceof HTMLInputElement || e instanceof HTMLTextAreaElement); try { const selectors: unknown = JSON.parse(text || "[]"); if (!Array.isArray(selectors) || !selectors.every(selector => typeof selector === "string")) return { status: "checked", allowed: false, reason: "invalid-private-selectors" }; - const restored = selectors.some(selector => [...document.querySelectorAll(selector)].some(e => (e instanceof HTMLInputElement || e instanceof HTMLTextAreaElement) && e.value)); - if (populated()) return { status: "checked", allowed: false, reason: "populated-private-input" }; + const restored = selectors.some(selector => fields(selector).some(e => e.value)); + if (fields("input,textarea").some(populatedPrivate)) return { status: "checked", allowed: false, reason: "populated-private-input" }; if (restored) return { status: "checked", allowed: false, reason: "restored-private-selector" }; return { status: "checked", allowed: true }; } catch { @@ -30,6 +44,15 @@ export function pageControl(operation: "observe" | "act" | "capture-check" | "do } } + const preparedFile = ["verify-file", "cancel-file"].includes(operation) ? world.__opzeroFile : undefined; + if (operation === "cancel-file") { + if (!preparedFile || preparedFile.token !== token) return refused("file-cancel-refused"); + delete world.__opzeroFile; + try { preparedFile.node.removeEventListener("change", preparedFile.listener); } catch {} + try { preparedFile.node.removeAttribute(preparedFile.attribute); } catch {} + return { status: "cancelled", retry: false }; + } + const prepared = operation === "submit" ? world.__opzeroSubmit : undefined; if (operation === "submit" || operation === "prepare-submit") delete world.__opzeroSubmit; if (operation !== "submit" && world.__opzeroPrivateDocument) return refused("private-quarantine"); @@ -57,15 +80,39 @@ export function pageControl(operation: "observe" | "act" | "capture-check" | "do const inputs = roots.flatMap(root => [...root.querySelectorAll("input,textarea")]) .filter((e): e is HTMLInputElement | HTMLTextAreaElement => e instanceof HTMLInputElement || e instanceof HTMLTextAreaElement); const privateNodes = new Set(inputs.filter(privateInput)); + const restoredNodes = new Set(); try { if (!Array.isArray(options.selectors) || !options.selectors.every(s => typeof s === "string")) return refused("invalid-private-selectors"); for (const selector of options.selectors) for (const root of roots) for (const e of root.querySelectorAll(selector)) { - if (e instanceof HTMLInputElement || e instanceof HTMLTextAreaElement) privateNodes.add(e); + if (e instanceof HTMLInputElement || e instanceof HTMLTextAreaElement) { privateNodes.add(e); restoredNodes.add(e); } } } catch { return refused("invalid-private-selectors"); } - const privacy = inputs.some(e => e.value && privateInput(e)) ? "populated-private-input" - : inputs.some(e => e.value && privateNodes.has(e)) ? "restored-private-selector" : null; + const privacy = inputs.some(populatedPrivate) ? "populated-private-input" + : inputs.some(e => e.value && restoredNodes.has(e)) ? "restored-private-selector" : null; if (operation !== "submit" && privacy) return refused(privacy); + if (operation === "verify-file") { + delete world.__opzeroFile; + const markerPresent = preparedFile?.node.hasAttribute(preparedFile.attribute) === true; + if (preparedFile) try { preparedFile.node.removeEventListener("change", preparedFile.listener); } catch {} + if (preparedFile) try { preparedFile.node.removeAttribute(preparedFile.attribute); } catch {} + if (!preparedFile || preparedFile.token !== token || preparedFile.expires < Date.now() || preparedFile.node.ownerDocument !== document + || !preparedFile.node.isConnected || !markerPresent || preparedFile.node.type !== "file") return refused("file-verification-refused"); + let metadata: unknown; + try { metadata = JSON.parse(text || "null"); } catch { return refused("invalid-file-metadata"); } + if (typeof metadata !== "object" || metadata === null || !("name" in metadata) || !("size" in metadata) + || typeof metadata.name !== "string" || typeof metadata.size !== "number") return refused("invalid-file-metadata"); + const files = preparedFile.node.files; + if (!files || files.length !== 1) return { status: "unknown", retry: false }; + const file = files[0]; + if (file.name !== metadata.name || file.size !== metadata.size || file.type !== "application/pdf") return { status: "unknown", retry: false }; + try { + if (preparedFile.changes === 0) { + preparedFile.node.dispatchEvent(new Event("input", { bubbles: true, composed: true })); + preparedFile.node.dispatchEvent(new Event("change", { bubbles: true })); + } + return { status: "attached", retry: false }; + } catch { return { status: "unknown", retry: false }; } + } const privateLabels = new Set(); for (const e of inputs) if (privateNodes.has(e)) { for (const label of e.labels ?? []) privateLabels.add(label); @@ -113,33 +160,72 @@ export function pageControl(operation: "observe" | "act" | "capture-check" | "do return normalize(labelled.length ? labelled.map(n => safeText(n)).join(" ") : e.getAttribute("aria-label") || (e instanceof HTMLInputElement || e instanceof HTMLTextAreaElement ? [...(e.labels ?? [])].map(n => safeText(n)).join(" ") || e.getAttribute("placeholder") || "" : safeText(e))); }; - const kind = (e: Element): "fill" | "click" | null => { + const actionLabel = (e: Element) => { + const own = label(e); + if (own || !(e instanceof HTMLButtonElement) || e.type !== "button" || !e.parentElement) return own; + const siblings = [...e.parentElement.childNodes].filter((node): node is Element => node instanceof Element); + const choices = siblings.filter(node => node.getAttribute("role") === "combobox" && available(node)); + const buttons = siblings.filter(node => node instanceof HTMLButtonElement); + return choices.length === 1 && buttons.length === 1 && buttons[0] === e && label(choices[0]) + ? `${label(choices[0])} options` : own; + }; + const publicFileLabel = (e: HTMLInputElement) => [...(e.labels ?? [])].find(candidate => { + const rect = candidate.getBoundingClientRect(); + return !privateLabels.has(candidate) && available(candidate) && rect.width > 1 && rect.height > 1 && label(candidate).length > 0; + }); + const fileAvailable = (e: HTMLInputElement) => { + const rect = typeof e.getBoundingClientRect === "function" ? e.getBoundingClientRect() : { width: 2, height: 2 }; + const labelledContext = elements.has(e) && !e.hasAttribute("inert") && !e.hasAttribute("hidden") && e.getAttribute("aria-hidden") !== "true" + && !e.parentElement?.shadowRoot && !(e.parentElement instanceof HTMLSlotElement && e.parentElement.assignedNodes().length) + && ancestry(e).slice(1).every(n => !(n instanceof Element) || exposed(n)) && modals.every(m => ancestry(e).includes(m)); + return e.type === "file" && ((available(e) && rect.width > 1 && rect.height > 1 && label(e).length > 0) || labelledContext && !!publicFileLabel(e)); + }; + const kind = (e: Element): "fill" | "click" | "upload" | null => { if (ancestry(e).some(n => n instanceof Element && (privateNodes.has(n) || privateLabels.has(n)))) return null; if (e instanceof HTMLTextAreaElement) return "fill"; - if (e instanceof HTMLInputElement) return ["text", "email", "search", "tel", "url", "number"].includes(e.type) ? "fill" : ["button", "submit"].includes(e.type) ? "click" : null; - return e.matches("button,a[href],[role=button]") ? "click" : null; + if (e instanceof HTMLInputElement) return e.type === "file" && fileAvailable(e) ? "upload" : ["text", "email", "search", "tel", "url", "number"].includes(e.type) ? "fill" : ["button", "submit", "checkbox", "radio"].includes(e.type) ? "click" : null; + return e.matches("button,a[href],[role=button],[role=option],[role=menuitem],[role=menuitemcheckbox],[role=menuitemradio],[role=checkbox],[role=radio]") ? "click" : null; }; const disabled = (e: Element) => e.matches(":disabled") || ancestry(e).some(n => n instanceof Element && n.getAttribute("aria-disabled") === "true") || (e instanceof HTMLInputElement || e instanceof HTMLTextAreaElement) && e.readOnly; const form = (e: Element) => e instanceof HTMLButtonElement || e instanceof HTMLInputElement || e instanceof HTMLTextAreaElement ? e.form : null; - const semantics = (e: Element) => JSON.stringify([location.href, kind(e), label(e), - ["role", "type", "href", "target", "disabled", "aria-disabled", "aria-checked", "aria-expanded", "aria-pressed", "readonly", "min", "max", "step", "pattern", "maxlength"].map(a => e.getAttribute(a)), - disabled(e), e instanceof HTMLInputElement || e instanceof HTMLTextAreaElement ? e.value : null, - (e instanceof HTMLButtonElement || e instanceof HTMLInputElement) && e.form ? [e.formAction, e.form.action, e.formTarget, e.form.target, e.form.method] : null, - ancestry(e).filter((n): n is Element => n instanceof Element).map(n => [n.getAttribute("inert"), n.getAttribute("aria-hidden"), n.getAttribute("aria-disabled"), n.getAttribute("role")])]); + const semantics = (e: Element, submitReadiness = false) => JSON.stringify([location.href, kind(e), actionLabel(e), + ["role", "type", "href", "target", "disabled", "aria-disabled", "aria-checked", "aria-selected", "aria-expanded", "aria-pressed", "readonly", "min", "max", "step", "pattern", "maxlength"].map(a => submitReadiness && a === "disabled" ? null : e.getAttribute(a)), + submitReadiness ? null : disabled(e), e instanceof HTMLInputElement ? [e.value, e.checked, e.indeterminate] : e instanceof HTMLTextAreaElement ? e.value : null, + (e instanceof HTMLButtonElement || e instanceof HTMLInputElement) && e.form ? [e.formAction, e.form.action, e.formTarget, e.form.target, e.form.method, + e.formMethod, e.formEnctype, e.form.enctype, e.formNoValidate, e.form.noValidate] : null, + ancestry(e).filter((n): n is Element => n instanceof Element).map(n => [n.getAttribute("inert"), n.getAttribute("aria-hidden"), n.getAttribute("aria-disabled"), n.getAttribute("role"), submitReadiness && n === e ? null : n.getAttribute("disabled")])]); const target = (node: Element, id: string): Target => ({ node, root: node.getRootNode(), form: form(node), ancestry: ancestry(node), marker: semantics(node), - action: { id, kind: kind(node)!, label: label(node).slice(0, 160), role: (node.getAttribute("role") || node.localName).slice(0, 80), disabled: disabled(node) } }); - const fresh = (t: Target) => t.node.isConnected && t.node.ownerDocument === document && elements.has(t.node) && !privateNodes.has(t.node) - && t.root === t.node.getRootNode() && t.form === form(t.node) && available(t.node) && !disabled(t.node) && t.marker === semantics(t.node) + action: { id, kind: kind(node)!, label: actionLabel(node).slice(0, 160), role: (node.getAttribute("role") || node.localName).slice(0, 80), disabled: disabled(node) } }); + const fresh = (t: Target, mode: "action" | "prepare-submit" | "submit" = "action") => t.node.isConnected && t.node.ownerDocument === document && elements.has(t.node) && !privateNodes.has(t.node) + && t.root === t.node.getRootNode() && t.form === form(t.node) && (t.action.kind === "upload" && t.node instanceof HTMLInputElement ? fileAvailable(t.node) : available(t.node)) && (mode === "prepare-submit" || !disabled(t.node)) && t.marker === semantics(t.node, mode === "submit") && t.ancestry.length === ancestry(t.node).length && t.ancestry.every((n, i) => n === ancestry(t.node)[i]); const sameOriginSubmit = (node: HTMLButtonElement | HTMLInputElement) => node.type === "submit" && node.form && new URL(node.formAction || node.form.action).origin === expectedOrigin && (!(node.formTarget || node.form.target) || (node.formTarget || node.form.target) === "_self"); if (operation === "submit") { - if (!prepared || prepared.token !== token || prepared.expires < Date.now() || !world.__opzeroPrivateDocument || !world.__opzeroPrivateForms?.has(prepared.form) - || prepared.node.form !== prepared.form || !fresh(prepared.target) || !sameOriginSubmit(prepared.node)) return refused("private-submit-refused"); - try { prepared.node.click(); return { status: "executed", retry: false }; } catch { return { status: "unknown", retry: false }; } + const bound = !!prepared && prepared.token === token && prepared.expires >= Date.now() && !!world.__opzeroPrivateDocument + && !!world.__opzeroPrivateForms?.has(prepared.form) && prepared.node.form === prepared.form && fresh(prepared.target, "submit") && !!sameOriginSubmit(prepared.node); + if (prepared && bound) { + try { prepared.node.click(); return { status: "executed", retry: false }; } catch { return { status: "unknown", retry: false }; } + } + return refused("private-submit-refused"); + } + if (operation === "validate-file") { + const file = world.__opzeroFile; + if (!file || file.token !== token || file.expires < Date.now() || !file.node.hasAttribute(file.attribute) || !fresh(file.target)) return refused("file-validation-refused"); + return { status: "validated", retry: false }; } if (operation === "observe") { - const nodes = [...elements].filter(e => kind(e) && available(e)); + const nodes = [...elements].filter(e => kind(e) && (e instanceof HTMLInputElement && kind(e) === "upload" ? fileAvailable(e) : available(e))); + const popups = new Set([...elements].filter(e => available(e) && ["menu", "listbox", "dialog"].includes(e.getAttribute("role") || ""))); + for (const e of elements) if (available(e) && e.getAttribute("aria-expanded") === "true") { + const root = e.getRootNode(); + for (const id of (e.getAttribute("aria-controls") || "").split(/\s+/).filter(Boolean)) { + const popup = root instanceof ShadowRoot ? root.getElementById(id) : document.getElementById(id); + if (popup && available(popup)) popups.add(popup); + } + } + const inPopup = (e: Element) => ancestry(e).some(n => n instanceof Element && popups.has(n)); + nodes.sort((a, b) => Number(inPopup(b)) - Number(inPopup(a))); const targets = nodes.slice(0, 100).map((node, i) => target(node, String(i))); const body = options.controlsOnly ? "" : normalize(document.body ? safeText(document.body) : ""); const titleNode = document.querySelector("head > title"); @@ -147,17 +233,29 @@ export function pageControl(operation: "observe" | "act" | "capture-check" | "do world.__opzeroSnapshot = { token, origin: expectedOrigin, document, targets, expires: Date.now() + 30000 }; return { status: "observed", pageProtocolVersion: 2, url: location.href, title: title.slice(0, 200), text: body.slice(0, 12000), actions: targets.map(t => t.action), snapshot: token, mode: options.controlsOnly ? "controls-only" : "full", partial: opaqueCount > 0, opaqueSurfaces, - truncation: { text: body.length > 12000, actions: nodes.length > 100, opaqueSurfaces: opaqueCount > 100, labels: targets.some(t => label(t.node).length > 160 || (t.node.getAttribute("role") || "").length > 80), title: title.length > 200 } }; + truncation: { text: body.length > 12000, actions: nodes.length > 100, opaqueSurfaces: opaqueCount > 100, labels: targets.some(t => actionLabel(t.node).length > 160 || (t.node.getAttribute("role") || "").length > 80), title: title.length > 200 } }; } if (!snapshot || snapshot.token !== token || snapshot.origin !== expectedOrigin || snapshot.document !== document || snapshot.expires < Date.now()) return refused("stale"); const t = snapshot.targets.find(t => t.action.id === actionId); - if (!t || !fresh(t)) return refused("stale"); + if (!t || !fresh(t, operation === "prepare-submit" ? "prepare-submit" : "action")) return refused("stale"); const node = t.node, action = t.action; if (operation === "prepare-submit") { if (!(node instanceof HTMLButtonElement || node instanceof HTMLInputElement) || !sameOriginSubmit(node) || !node.form) return refused("not-same-origin-submit"); - const submitToken = crypto.randomUUID(); world.__opzeroSubmit = { token: submitToken, target: t, node, form: node.form, expires: Date.now() + 30000 }; - return { status: "prepared", submitToken }; + // Bound to one private credential read (60 s vault deadline) plus re-observation, fill, and readiness; never renewed. + const submitToken = crypto.randomUUID(), expiresInMs = 90000; + world.__opzeroSubmit = { token: submitToken, target: { ...t, marker: semantics(node, true) }, node, form: node.form, expires: Date.now() + expiresInMs }; + return { status: "prepared", submitToken, expiresInMs }; + } + if (operation === "prepare-file") { + if (action.kind !== "upload" || !(node instanceof HTMLInputElement) || node.type !== "file") return refused("not-file-action"); + const fileToken = crypto.randomUUID(), attribute = `data-opzero-file-${crypto.randomUUID().replace(/-/g, "")}`; + try { node.setAttribute(attribute, ""); } catch { return refused("file-preparation-refused"); } + const file: PreparedFile = { token: fileToken, target: t, node, attribute, expires: Date.now() + 30000, changes: 0, listener: () => { file.changes++; } }; + try { node.addEventListener("change", file.listener); } catch { try { node.removeAttribute(attribute); } catch {} return refused("file-preparation-refused"); } + world.__opzeroFile = file; + return { status: "prepared", fileToken, selector: `[${attribute}]` }; } + if (action.kind === "upload") return refused("file-requires-upload-rpc"); if (action.kind === "fill") { if (typeof text !== "string" || text.length > 2000 || !(node instanceof HTMLInputElement || node instanceof HTMLTextAreaElement)) return refused("invalid-fill"); if (node instanceof HTMLInputElement && node.type === "number" && text !== "" && (!Number.isFinite(Number(text)) || !/^-?(?:\d+(?:\.\d+)?|\.\d+)(?:e[+-]?\d+)?$/i.test(text))) return refused("invalid-fill"); diff --git a/src/native-host/transport.ts b/src/native-host/transport.ts index fbeeb12..c4e388b 100644 --- a/src/native-host/transport.ts +++ b/src/native-host/transport.ts @@ -18,6 +18,7 @@ function string(value: unknown): string { if (typeof value !== "string") throw n export type OwnedPage = Readonly<{ tabId: number; origin: string }>; export type PrivateSubmission = Readonly<{ submitToken: string; documentId: string }>; export type ActionOutcome = { status: "executed" | "not-executed" | "unknown"; retry: false; reason?: string }; +export type UploadOutcome = { status: "attached" | "not-executed" | "unknown"; retry: false; name?: string; mime?: "application/pdf"; size?: number }; export type RecordingReceipt = { path: string | null; directory: string; seconds: number; frames: { file: string; seconds: number; sha256: string }[]; error: string | null; encodeMs: number; captureMs: number; sampleFps: number }; export class ChromeTransport { @@ -126,6 +127,18 @@ export class ChromeTransport { if (result.status !== "executed" && result.status !== "not-executed" && result.status !== "unknown") throw new Error("Invalid action result; no replay"); return { status: result.status, retry: false, ...(typeof result.reason === "string" ? { reason: result.reason } : {}) }; } + async uploadFile(page: OwnedPage, snapshot: string, actionId: string, filePath: string): Promise { + if (this.#recordings.has(page.tabId) || !path.isAbsolute(filePath) || filePath.includes("\0") || path.extname(filePath).toLowerCase() !== ".pdf") throw new Error("Valid local PDF required and recording must be stopped"); + const info = await fs.lstat(filePath); + if (!info.isFile() || info.isSymbolicLink() || info.uid !== process.getuid?.() || info.size < 1) throw new Error("Current-user-owned regular PDF required"); + const handle = await fs.open(filePath, "r"); + try { const magic = Buffer.alloc(5); if ((await handle.read(magic, 0, 5, 0)).bytesRead !== 5 || magic.toString() !== "%PDF-") throw new Error("PDF signature required"); } + finally { await handle.close(); } + const name = path.basename(filePath); + const result = object(await this.#call("uploadFile", { ...this.#owned(page), snapshot, actionId, path: filePath, name, size: info.size })); + if (result.status !== "attached" && result.status !== "not-executed" && result.status !== "unknown") throw new Error("Invalid upload result; no replay"); + return result.status === "attached" ? { status: "attached", retry: false, name, mime: "application/pdf", size: info.size } : { status: result.status, retry: false }; + } async privateFill(page: OwnedPage, selectors: string[], values: string[], options: { allowInsecureLoopback?: boolean } = {}) { if (this.#recordings.has(page.tabId)) throw new Error("Stop recording before private input"); const params = this.#owned(page); diff --git a/src/shared/page-protocol.ts b/src/shared/page-protocol.ts index b5c4837..2a88736 100644 --- a/src/shared/page-protocol.ts +++ b/src/shared/page-protocol.ts @@ -1,4 +1,4 @@ -export type PageAction = { id: string; kind: "fill" | "click"; label: string; role: string; disabled: boolean }; +export type PageAction = { id: string; kind: "fill" | "click" | "upload"; label: string; role: string; disabled: boolean }; export type Observation = { status: "observed"; pageProtocolVersion: 2; snapshot: string; url: string; title: string; text: string; actions: PageAction[]; mode: "full" | "controls-only"; partial: boolean; @@ -16,7 +16,7 @@ export function parseObservation(value: unknown): Observation { || !Array.isArray(raw.actions) || raw.actions.length > 100 || !Array.isArray(raw.opaqueSurfaces) || raw.opaqueSurfaces.length > 100) fail(); const actions = (raw.actions as unknown[]).map((value): PageAction => { const a = object(value); - if (a.kind !== "fill" && a.kind !== "click") return fail(); + if (a.kind !== "fill" && a.kind !== "click" && a.kind !== "upload") return fail(); return { id: string(a.id, 100), kind: a.kind, label: string(a.label, 160), role: string(a.role, 80), disabled: bool(a.disabled) }; }); const opaqueSurfaces = (raw.opaqueSurfaces as unknown[]).map((value, index) => { diff --git a/tests/security/background.test.ts b/tests/security/background.test.ts index aa4037c..49c340e 100644 --- a/tests/security/background.test.ts +++ b/tests/security/background.test.ts @@ -84,6 +84,56 @@ it("rescans restored selectors on observation and action with no public quaranti expect(b.chrome.scripting.executeScript.mock.calls.some(([r]: any) => r.args[0] === "act")).toBe(false); }); +it.each([12, 4 * 1024 * 1024 + 1, 64 * 1024 * 1024 + 1])("uploads %i bytes through one exact marker and confirms metadata once", async (size) => { + const b = await background(); + const owner = { session_id: "owner", turn_id: "turn", tabId: 1, expectedOrigin: "https://synthetic.invalid" }; + await b.rpc("createTab", owner); await b.rpc("attach", owner); await b.rpc("bindPage", owner); + b.chrome.scripting.executeScript.mockImplementation((request: any, cb: Function) => { + const operation = request.args[0]; + const result = operation === "document-check" ? { status: "checked" } + : operation === "observe" ? { status: "observed", snapshot: request.args[2] } + : operation === "prepare-file" ? { status: "prepared", fileToken: "file-token", selector: "[data-marker]" } + : operation === "validate-file" ? { status: "validated" } + : operation === "verify-file" ? { status: "attached" } : { status: "cancelled" }; + cb([{ documentId: "doc-1", frameId: 0, result }]); + }); + b.chrome.debugger.sendCommand.mockImplementation((_: unknown, method: string, __: unknown, cb: Function) => cb( + method === "DOM.getDocument" ? { root: { nodeId: 7 } } : method === "DOM.querySelectorAll" ? { nodeIds: [9] } : {})); + const snapshot = (await b.rpc("observePage", owner)).result.snapshot; + expect((await b.rpc("uploadFile", { ...owner, snapshot, actionId: "0", path: "/owned/invoice.pdf", name: "invoice.pdf", size })).result).toEqual({ status: "attached", retry: false }); + expect(b.chrome.debugger.sendCommand.mock.calls.map(call => call[1])).toEqual(["DOM.getDocument", "DOM.querySelectorAll", "DOM.setFileInputFiles"]); + expect(b.chrome.debugger.sendCommand.mock.calls[2][2]).toEqual({ nodeId: 9, files: ["/owned/invoice.pdf"] }); + expect((await b.rpc("uploadFile", { ...owner, snapshot, actionId: "0", path: "/owned/invoice.pdf", name: "invoice.pdf", size })).result.status).toBe("not-executed"); +}); + +it("fails closed before dispatch for duplicate file markers and while recording", async () => { + const b = await background(); const owner = { session_id: "owner", turn_id: "turn", tabId: 1, expectedOrigin: "https://synthetic.invalid" }; + await b.rpc("createTab", owner); await b.rpc("attach", owner); await b.rpc("bindPage", owner); + b.chrome.scripting.executeScript.mockImplementation((request: any, cb: Function) => cb([{ documentId: "doc-1", frameId: 0, result: + request.args[0] === "document-check" ? { status: "checked", allowed: true } : request.args[0] === "observe" ? { status: "observed", snapshot: request.args[2] } : request.args[0] === "prepare-file" ? { status: "prepared", fileToken: "token", selector: "[marker]" } : { status: "cancelled" } }])); + b.chrome.debugger.sendCommand.mockImplementation((_: unknown, method: string, __: unknown, cb: Function) => cb(method === "DOM.getDocument" ? { root: { nodeId: 1 } } : { nodeIds: [2, 3] })); + let snapshot = (await b.rpc("observePage", owner)).result.snapshot; + expect((await b.rpc("uploadFile", { ...owner, snapshot, actionId: "0", path: "/x.pdf", name: "x.pdf", size: 1 })).result.status).toBe("not-executed"); + expect(b.chrome.debugger.sendCommand.mock.calls.some(call => call[1] === "DOM.setFileInputFiles")).toBe(false); + await b.rpc("recordingState", { ...owner, active: true }); snapshot = (await b.rpc("observePage", owner)).result.snapshot; + expect((await b.rpc("uploadFile", { ...owner, snapshot, actionId: "0", path: "/x.pdf", name: "x.pdf", size: 1 })).result.status).toBe("not-executed"); +}); + +it("invalidates file upload on navigation, privacy quarantine, and ownership mismatch", async () => { + const b = await background(); const owner = { session_id: "owner", turn_id: "turn", tabId: 1, expectedOrigin: "https://synthetic.invalid" }; + await b.rpc("createTab", owner); await b.rpc("attach", owner); await b.rpc("bindPage", owner); + b.chrome.scripting.executeScript.mockImplementation((request: any, cb: Function) => cb([{ documentId: "doc-1", frameId: 0, result: + request.args[0] === "document-check" ? { status: "checked" } : { status: "observed", snapshot: request.args[2] } }])); + let snapshot = (await b.rpc("observePage", owner)).result.snapshot; + b.chrome.tabs.onUpdated.emit(1, { status: "loading" }); + expect((await b.rpc("uploadFile", { ...owner, snapshot, actionId: "0", path: "/x.pdf", name: "x.pdf", size: 1 })).result.status).toBe("not-executed"); + snapshot = (await b.rpc("observePage", owner)).result.snapshot; + b.chrome.storage.local.set({ "PRIVATE_CAPTURE_QUARANTINE:1": { documentId: "doc-1", selectors: [] } }, () => {}); + expect((await b.rpc("uploadFile", { ...owner, snapshot, actionId: "0", path: "/x.pdf", name: "x.pdf", size: 1 })).result.status).toBe("not-executed"); + expect((await b.rpc("uploadFile", { ...owner, session_id: "other", snapshot, actionId: "0", path: "/x.pdf", name: "x.pdf", size: 1 })).error).toBeDefined(); + expect(b.chrome.debugger.sendCommand).not.toHaveBeenCalled(); +}); + it("creates an inactive tab without activating a window", async () => { const b = await background(); const response = await b.rpc("createTab", { session_id: "owner", turn_id: "turn" }); @@ -91,6 +141,23 @@ it("creates an inactive tab without activating a window", async () => { expect(b.chrome.tabs.create.mock.calls[0][0].active).toBe(false); }); +it("confirms a session group title from Chrome instead of trusting dispatch", async () => { + const b = await background(); + const params = { session_id: "owner", turn_id: "turn" }; + expect((await b.rpc("nameSession", { ...params, name: "Before group" })).result).toEqual({ name: "Before group", confirmed: false }); + await b.rpc("createTab", params); + expect((await b.rpc("nameSession", { ...params, name: "agent2 · Preview 1704" })).result).toEqual({ name: "agent2 · Preview 1704", confirmed: true }); + b.chrome.tabGroups.get.mockImplementation((_: unknown, cb: Function) => cb({ id: 1, title: "Wrong title" })); + const refused = await b.rpc("nameSession", { ...params, name: "agent2 · Preview 1705" }); + expect(refused.result).toBeUndefined(); + expect(refused.error.message).toBe("Session group title confirmation failed"); + b.chrome.tabGroups.get.mockImplementation((_: unknown, cb: Function) => cb({ id: 1, title: "agent2 · Preview 1704" })); + await b.rpc("createTab", params); + expect(b.chrome.tabGroups.update.mock.calls.at(-1)?.[1].title).toBe("agent2 · Preview 1704"); + b.chrome.tabGroups.update.mockImplementationOnce(() => { throw new Error("synthetic update failure"); }); + expect((await b.rpc("nameSession", { ...params, name: "agent2 · Preview 1706" })).error).toBeDefined(); +}); + it("advertises protocol v2 and gates profile reads after revocation", async () => { const b = await background(); expect((await b.rpc("getInfo")).result.protocolVersion).toBe(2); diff --git a/tests/security/page-diagnostics.test.ts b/tests/security/page-diagnostics.test.ts index bd8463e..0bf7b57 100644 --- a/tests/security/page-diagnostics.test.ts +++ b/tests/security/page-diagnostics.test.ts @@ -11,7 +11,10 @@ it("allows embedded surfaces while preserving fixed private diagnostics", () => vi.stubGlobal("location", { origin: "https://synthetic.invalid" }); vi.stubGlobal("HTMLInputElement", Input); vi.stubGlobal("HTMLTextAreaElement", class {}); + vi.stubGlobal("HTMLElement", class {}); + vi.stubGlobal("chrome", { dom: { openOrClosedShadowRoot: () => null } }); vi.stubGlobal("document", { + childNodes: [], querySelectorAll: (selector: string) => { if (selector === "[") throw new Error("synthetic-secret"); return inputs; diff --git a/tests/security/private-input.browser.test.ts b/tests/security/private-input.browser.test.ts index f75d710..248256b 100644 --- a/tests/security/private-input.browser.test.ts +++ b/tests/security/private-input.browser.test.ts @@ -19,7 +19,7 @@ describe.skipIf(!executablePath)("private guard in disposable headless Chrome (l let fillPrivateFields: Function; const server = http.createServer((_, response) => { response.setHeader("Content-Type", "text/html"); - response.end('Synthetic only'); + response.end('Synthetic onlyunchanged'); }); async function isolated() { const tree = await cdp.send("Page.getFrameTree"); @@ -27,7 +27,7 @@ describe.skipIf(!executablePath)("private guard in disposable headless Chrome (l } async function run(fn: Function, args: unknown[]) { const result = await cdp.send("Runtime.callFunctionOn", { - executionContextId: contextId, functionDeclaration: fn.toString(), arguments: args.map(value => ({ value })), returnByValue: true + executionContextId: contextId, functionDeclaration: fn.toString(), arguments: args.map(value => ({ value })), returnByValue: true, awaitPromise: true }); if (result.exceptionDetails) throw new Error("Synthetic isolated script failed"); return result.result.value; @@ -74,12 +74,257 @@ describe.skipIf(!executablePath)("private guard in disposable headless Chrome (l expect(await fill()).toEqual({ status: "refused" }); }); - it("rejects a public action after an input value changes without a DOM mutation", async () => { + it("rejects a public fill after its input value changes without a DOM mutation", async () => { await reset(); + await cdp.send("Runtime.evaluate", { contextId, expression: "globalThis.chrome = { dom: { openOrClosedShadowRoot: element => element.shadowRoot } }" }); const snapshot = await run(pageControl, ["observe", origin, "public-snapshot"]); - const button = snapshot.actions.find((action: { label: string }) => action.label === "Test"); + const input = snapshot.actions.find((action: { kind: string }) => action.kind === "fill"); await page.evaluate(() => { (document.querySelector("#user") as HTMLInputElement).value = "changed-outside-driver"; }); - expect(await run(pageControl, ["act", origin, "public-snapshot", button.id])).toMatchObject({ status: "not-executed", reason: "stale" }); + expect(await run(pageControl, ["act", origin, "public-snapshot", input.id, "replacement"])).toMatchObject({ status: "not-executed", reason: "stale" }); + expect(await page.locator("#user").inputValue()).toBe("changed-outside-driver"); + }); + + it("discovers and dispatches native fallback controls in Chromium", async () => { + await reset(); + await cdp.send("Runtime.evaluate", { contextId, expression: "globalThis.chrome = { dom: { openOrClosedShadowRoot: element => element.shadowRoot } }" }); + const labels = ["option", "menuitem", "menuitemcheckbox", "menuitemradio", "checkbox", "radio"]; + await page.evaluate(roles => { + for (const role of roles) { + const item = document.createElement("div"); + item.role = role; + item.textContent = `Select ${role}`; + item.addEventListener("click", () => { item.dataset.clicked = "true"; }); + document.body.append(item); + } + for (const type of ["checkbox", "radio"]) { + const item = document.createElement("input"); + item.type = type; + item.setAttribute("aria-label", `Select native ${type}`); + document.body.append(item); + } + }, labels); + for (const label of [...labels.map(role => `Select ${role}`), "Select native checkbox", "Select native radio"]) { + const token = `control-${label}`; + const snapshot = await run(pageControl, ["observe", origin, token]); + const action = snapshot.actions.find((candidate: { label: string }) => candidate.label === label); + expect(action).toMatchObject({ kind: "click", label }); + expect(await run(pageControl, ["act", origin, token, action.id])).toMatchObject({ status: "executed", retry: false }); + } + expect(await page.evaluate(() => ({ + aria: [...document.querySelectorAll("[role]")].every(item => (item as HTMLElement).dataset.clicked === "true"), + native: [...document.querySelectorAll('input[type="checkbox"],input[type="radio"]')].every(item => (item as HTMLInputElement).checked) + }))).toEqual({ aria: true, native: true }); + }); + + it("opens and selects an Admin-shaped Rule dropdown from observed actions", async () => { + await reset(); + await cdp.send("Runtime.evaluate", { contextId, expression: "globalThis.chrome = { dom: { openOrClosedShadowRoot: element => element.shadowRoot } }" }); + await page.evaluate(() => { + const form = document.createElement("div"); + form.innerHTML = '
Deny
'; + const caret = form.querySelector("button")!; + caret.addEventListener("click", () => { + const menu = document.createElement("div"); + menu.setAttribute("role", "menu"); + for (const choice of ["Allow", "Deny"]) { + const item = document.createElement("div"); + item.setAttribute("role", "menuitem"); + item.textContent = choice; + item.addEventListener("click", () => { form.querySelector("#rule-value")!.textContent = choice; menu.remove(); }); + menu.append(item); + } + document.body.append(menu); + }); + document.body.append(form); + }); + const before = await run(pageControl, ["observe", origin, "rule-closed"]); + const caret = before.actions.filter((action: { label: string }) => action.label === "Rule Deny options"); + expect(caret).toHaveLength(1); + expect(caret[0]).toMatchObject({ kind: "click", role: "button" }); + expect(await run(pageControl, ["act", origin, "rule-closed", caret[0].id])).toMatchObject({ status: "executed" }); + const opened = await run(pageControl, ["observe", origin, "rule-opened"]); + const allow = opened.actions.filter((action: { label: string }) => action.label === "Allow"); + expect(allow).toHaveLength(1); + expect(await run(pageControl, ["act", origin, "rule-opened", allow[0].id])).toMatchObject({ status: "executed" }); + const selected = await run(pageControl, ["observe", origin, "rule-selected"]); + expect(selected.actions).toContainEqual(expect.objectContaining({ label: "Rule Allow options" })); + expect(await page.locator("#rule-value").textContent()).toBe("Allow"); + }); + + it("keeps an expanded filter's portaled options ahead of a long table", async () => { + await reset(); + await cdp.send("Runtime.evaluate", { contextId, expression: "globalThis.chrome = { dom: { openOrClosedShadowRoot: element => element.shadowRoot } }" }); + await page.evaluate(() => { + document.body.innerHTML = ''; + for (let i = 0; i < 120; i++) { + const row = document.createElement("button"); row.textContent = `Transaction ${i}`; document.body.append(row); + } + const popup = document.createElement("div"); popup.id = "cardholders"; popup.setAttribute("role", "dialog"); + popup.innerHTML = ''; + popup.querySelector("button")!.addEventListener("click", event => (event.currentTarget as HTMLElement).setAttribute("aria-pressed", "true")); + document.body.append(popup); + }); + const snapshot = await run(pageControl, ["observe", origin, "filter-open"]); + expect(snapshot.truncation.actions).toBe(true); + const option = snapshot.actions.find((action: { label: string }) => action.label === "afif test"); + expect(option).toBeDefined(); + expect(await run(pageControl, ["act", origin, "filter-open", option.id])).toMatchObject({ status: "executed" }); + expect(await page.locator("#cardholders button").getAttribute("aria-pressed")).toBe("true"); + }); + + it("observes a disabled display-only password mask without exposing or enabling its field", async () => { + await reset(); + await cdp.send("Runtime.evaluate", { contextId, expression: "globalThis.chrome = { dom: { openOrClosedShadowRoot: element => element.shadowRoot } }" }); + await page.evaluate(() => { + const password = document.querySelector("#password") as HTMLInputElement; + password.defaultValue = "••••••••"; password.disabled = true; + const title = document.createElement("h1"); title.textContent = "Personal settings"; document.body.append(title); + }); + const snapshot = await run(pageControl, ["observe", origin, "masked-settings"]); + expect(snapshot).toMatchObject({ status: "observed", text: expect.stringContaining("Personal settings") }); + expect(snapshot.actions.some((action: { role: string }) => action.role === "password")).toBe(false); + expect(JSON.stringify(snapshot)).not.toContain("••••••••"); + expect(await run(pageControl, ["capture-check", origin, ""])).toMatchObject({ allowed: true }); + await page.evaluate(() => { (document.querySelector("#password") as HTMLInputElement).value = "PRIVATE_CANARY"; }); + expect(await run(pageControl, ["observe", origin, "changed"])).toMatchObject({ reason: "populated-private-input" }); + expect(await run(pageControl, ["capture-check", origin, ""])).toMatchObject({ allowed: false }); + }); + + it.each(["enabled", "transferred", "restored"])("still blocks a %s password mask", async mode => { + await reset(); + await cdp.send("Runtime.evaluate", { contextId, expression: "globalThis.chrome = { dom: { openOrClosedShadowRoot: element => element.shadowRoot } }" }); + await page.evaluate(disabled => { + const password = document.querySelector("#password") as HTMLInputElement; + password.defaultValue = "••••••••"; password.disabled = disabled; + }, mode !== "enabled"); + if (mode === "transferred") await cdp.send("Runtime.evaluate", { contextId, expression: 'globalThis.__opzeroPrivateNodes = new WeakSet([document.querySelector("#password")])' }); + const selectors = mode === "restored" ? ["#password"] : []; + expect(await run(pageControl, ["observe", origin, "blocked", "", null, { selectors, controlsOnly: false }])).toMatchObject({ status: "refused" }); + expect(await run(pageControl, ["capture-check", origin, "", "", JSON.stringify(selectors)])).toMatchObject({ allowed: false }); + }); + + it.each(["enabled", "still-disabled", "replaced", "form-changed"])("binds a private submit that starts disabled: %s", async outcome => { + await reset(); + await cdp.send("Runtime.evaluate", { contextId, expression: "globalThis.chrome = { dom: { openOrClosedShadowRoot: element => element.shadowRoot } }" }); + await page.evaluate(() => { + const form = document.createElement("form"); form.action = location.href; + const button = document.querySelector("button")!; button.type = "submit"; button.disabled = true; + form.append(document.querySelector("#user")!, document.querySelector("#password")!, button); + form.addEventListener("submit", event => { event.preventDefault(); form.dataset.submitted = "true"; }); + document.body.append(form); + }); + const before = await run(pageControl, ["observe", origin, "disabled-login"]); + const button = before.actions.find((action: { label: string }) => action.label === "Test"); + expect(button.disabled).toBe(true); + const prepared = await run(pageControl, ["prepare-submit", origin, "disabled-login", button.id]); + expect(prepared).toMatchObject({ status: "prepared" }); + expect(await observe()).toMatchObject({ status: "observed" }); + expect(await fill()).toMatchObject({ status: "filled" }); + await page.evaluate(mode => { + const button = document.querySelector("button")!; + if (mode !== "still-disabled") button.disabled = false; + if (mode === "replaced") button.replaceWith(button.cloneNode(true)); + if (mode === "form-changed") button.form!.action = "https://other.invalid/login"; + }, outcome); + const submitted = await run(pageControl, ["submit", origin, prepared.submitToken]); + expect(submitted.status).toBe(outcome === "enabled" ? "executed" : "not-executed"); + expect(await page.locator("form").getAttribute("data-submitted")).toBe(outcome === "enabled" ? "true" : null); + expect((await run(pageControl, ["submit", origin, prepared.submitToken])).status).toBe("not-executed"); + }); + + async function preparedLogin(token: string, setup: (disabled: boolean) => void, disabled = false) { + await reset(); + await cdp.send("Runtime.evaluate", { contextId, expression: "globalThis.chrome = { dom: { openOrClosedShadowRoot: element => element.shadowRoot } }" }); + await page.evaluate(setup, disabled); + const before = await run(pageControl, ["observe", origin, token]); + const button = before.actions.find((action: { label: string }) => action.label === "Test"); + const prepared = await run(pageControl, ["prepare-submit", origin, token, button.id]); + expect(prepared).toMatchObject({ status: "prepared" }); + expect(await observe()).toMatchObject({ status: "observed" }); + expect(await fill()).toMatchObject({ status: "filled" }); + return prepared; + } + const loginForm = (disabled: boolean) => { + const form = document.createElement("form"); form.action = location.href; form.method = "post"; + const button = document.querySelector("button")!; button.type = "submit"; button.disabled = disabled; + form.append(document.querySelector("#user")!, document.querySelector("#password")!, button); + form.addEventListener("submit", event => { event.preventDefault(); form.dataset.submitted = String(Number(form.dataset.submitted || 0) + 1); }); + document.body.append(form); + if (disabled) document.querySelector("#password")!.addEventListener("input", () => setTimeout(() => { button.disabled = false; }, 600)); + }; + + it.each(["unchanged", "formmethod", "formenctype", "formnovalidate", "method", "enctype", "novalidate"])("binds a prepared login POST to its effective submission: %s", async change => { + const prepared = await preparedLogin(`effective-${change}`, loginForm); + await page.evaluate(change => { + const button = document.querySelector("button")!, form = button.form!; + if (change === "formmethod") button.formMethod = "get"; + if (change === "formenctype") button.formEnctype = "text/plain"; + if (change === "formnovalidate") button.formNoValidate = true; + if (change === "method") form.method = "get"; + if (change === "enctype") form.enctype = "multipart/form-data"; + if (change === "novalidate") form.noValidate = true; + }, change); + expect((await run(pageControl, ["submit", origin, prepared.submitToken])).status).toBe(change === "unchanged" ? "executed" : "not-executed"); + expect(await page.locator("form").getAttribute("data-submitted")).toBe(change === "unchanged" ? "1" : null); + }); + + it("refuses delayed validation and consumes the prepared login without replay", async () => { + const prepared = await preparedLogin("delayed-login", loginForm, true); + expect(prepared.expiresInMs).toBe(90000); + expect(await page.locator("form button").isDisabled()).toBe(true); + expect((await run(pageControl, ["submit", origin, prepared.submitToken])).status).toBe("not-executed"); + expect(await page.locator("form").getAttribute("data-submitted")).toBe(null); + await page.waitForTimeout(750); + expect(await page.locator("form button").isEnabled()).toBe(true); + expect((await run(pageControl, ["submit", origin, prepared.submitToken])).status).toBe("not-executed"); + expect(await page.locator("form").getAttribute("data-submitted")).toBe(null); + }); + + it.each(["password", "otp", "restored", "nested-otp", "closed-password", "display-mask"])("checks a %s control inside shadow roots before capture without returning values", async mode => { + await reset(); + await cdp.send("Runtime.evaluate", { contextId, expression: "globalThis.__closedRoots = new WeakMap(); globalThis.chrome = { dom: { openOrClosedShadowRoot: element => element.shadowRoot ?? globalThis.__closedRoots.get(element) ?? null } }" }); + await page.evaluate(mode => { + let root: ShadowRoot | null = null; + if (mode !== "closed-password") { const host = document.createElement("x-login"); document.body.append(host); root = host.attachShadow({ mode: "open" }); } + if (root && mode === "nested-otp") { const inner = document.createElement("x-otp"); root.append(inner); root = inner.attachShadow({ mode: "open" }); } + if (!root) return; + const input = document.createElement("input"); input.id = "shadow-private"; + if (mode === "password" || mode === "display-mask") input.type = "password"; + if (mode === "otp" || mode === "nested-otp") input.autocomplete = "one-time-code"; + if (mode === "display-mask") { input.defaultValue = "••••••••"; input.disabled = true; } else input.value = "PRIVATE_CANARY"; + root.append(input); + }, mode); + if (mode === "closed-password") await cdp.send("Runtime.evaluate", { contextId, expression: 'const host = document.createElement("x-closed"); document.body.append(host); const root = host.attachShadow({ mode: "closed" }); globalThis.__closedRoots.set(host, root); const input = document.createElement("input"); input.type = "password"; input.value = "PRIVATE_CANARY"; root.append(input);' }); + const checked = await run(pageControl, ["capture-check", origin, "", "", JSON.stringify(mode === "restored" ? ["#shadow-private"] : [])]); + expect(checked).toEqual(mode === "display-mask" ? { status: "checked", allowed: true } + : { status: "checked", allowed: false, reason: mode === "restored" ? "restored-private-selector" : "populated-private-input" }); + expect(JSON.stringify(checked)).not.toContain("CANARY"); + }); + + it.each([0, 64 * 1024 * 1024])("attaches a harmless PDF through a Direct-shaped wrapping upload label (%i padding bytes)", async (padding) => { + await reset(); + await cdp.send("Runtime.evaluate", { contextId, expression: "globalThis.chrome = { dom: { openOrClosedShadowRoot: element => element.shadowRoot } }" }); + const pdf = path.join(directory, "public-invoice.pdf"); + const bytes = Buffer.from("%PDF-1.4\n1 0 obj\n<<>>\nendobj\ntrailer\n<<>>\n%%EOF\n"); + fs.writeFileSync(pdf, bytes, { mode: 0o600 }); + const size = bytes.length + padding; + fs.truncateSync(pdf, size); + const snapshot = await run(pageControl, ["observe", origin, "upload-snapshot"]); + const action = snapshot.actions.find((candidate: { label: string }) => candidate.label === "Upload Invoice"); + expect(action).toMatchObject({ kind: "upload", role: "input", disabled: false }); + const prepared = await run(pageControl, ["prepare-file", origin, "upload-snapshot", action.id]); + expect(prepared).toMatchObject({ status: "prepared" }); + const root = await cdp.send("DOM.getDocument", { depth: 0, pierce: true }); + const matches = await cdp.send("DOM.querySelectorAll", { nodeId: root.root.nodeId, selector: prepared.selector }); + expect(matches.nodeIds).toHaveLength(1); + expect(await run(pageControl, ["validate-file", origin, prepared.fileToken])).toMatchObject({ status: "validated" }); + await cdp.send("DOM.setFileInputFiles", { nodeId: matches.nodeIds[0], files: [pdf] }); + expect(await run(pageControl, ["verify-file", origin, prepared.fileToken, "", JSON.stringify({ name: path.basename(pdf), size })])).toEqual({ status: "attached", retry: false }); + expect(await page.evaluate(() => { + const input = document.querySelector("#invoice") as HTMLInputElement; + const state = document.querySelector("#upload-state") as HTMLOutputElement; + return { count: input.files?.length, name: input.files?.[0]?.name, size: input.files?.[0]?.size, type: input.files?.[0]?.type, state: state.textContent, changes: state.dataset.changes }; + })).toEqual({ count: 1, name: "public-invoice.pdf", size, type: "application/pdf", state: "changed", changes: "1" }); }); it("atomically rejects a mismatching origin even with an otherwise valid observation", async () => { diff --git a/tests/security/scoped-observation.test.ts b/tests/security/scoped-observation.test.ts index 8e234ea..f65543f 100644 --- a/tests/security/scoped-observation.test.ts +++ b/tests/security/scoped-observation.test.ts @@ -9,7 +9,7 @@ const origin = "https://synthetic.invalid"; const observe = (controlsOnly = false, selectors: string[] = []) => pageControl("observe", origin, "fresh", "", null, { controlsOnly, selectors }); const act = (id = "0", text?: string) => pageControl("act", origin, "fresh", id, text); beforeEach(() => { doc = dom(); }); -afterEach(() => { for (const key of ["__opzeroSnapshot", "__opzeroSubmit", "__opzeroPrivateDocument", "__opzeroPrivateForms", "__opzeroPrivateNodes"]) Reflect.deleteProperty(globalThis, key); vi.unstubAllGlobals(); }); +afterEach(() => { vi.useRealTimers(); for (const key of ["__opzeroSnapshot", "__opzeroSubmit", "__opzeroFile", "__opzeroPrivateDocument", "__opzeroPrivateForms", "__opzeroPrivateNodes"]) Reflect.deleteProperty(globalThis, key); vi.unstubAllGlobals(); }); it("excludes embed fallback and closed hosts from text, labels, inventory and markers", () => { const button = new Button("Public"); @@ -36,6 +36,120 @@ it("fills public input next to embeds and tolerates unrelated text updates", () expect(act("0", "Public value").status).toBe("executed"); expect(input.value).toBe("Public value"); }); +it("discovers only named visible or visibly labelled file inputs", () => { + const hidden = new Input(), labelled = new Input(), label = new Element("label", "Upload statement"), visible = new Input(), tiny = new Input(); + const emptyLabelled = new Input(), emptyLabel = new Element("label"), hiddenParent = new Element("div"), nested = new Input(), nestedLabel = new Element("label", "Hidden ancestor"); + hidden.type = labelled.type = visible.type = tiny.type = emptyLabelled.type = nested.type = "file"; hidden.style.display = labelled.style.display = emptyLabelled.style.display = nested.style.display = "none"; + labelled.labels = [label]; label.append(labelled); visible.setAttribute("aria-label", "Upload invoice"); tiny.setAttribute("aria-label", "Tiny generic input"); tiny.getBoundingClientRect = () => ({ width: 1, height: 1 }); + emptyLabelled.labels = [emptyLabel]; emptyLabel.append(emptyLabelled); nested.labels = [nestedLabel]; hiddenParent.setAttribute("inert", ""); hiddenParent.append(nested); doc.body.append(hidden, label, visible, tiny, emptyLabel, hiddenParent, nestedLabel); + expect(parseObservation(observe()).actions).toEqual([ + expect.objectContaining({ kind: "upload", label: "Upload statement" }), + expect.objectContaining({ kind: "upload", label: "Upload invoice" }) + ]); +}); + +it("keeps file attachment behind a fresh single-use prepared capability", () => { + const input = new Input(); input.type = "file"; input.setAttribute("aria-label", "Upload PDF"); doc.body.append(input); + const observation = parseObservation(observe()); expect(observation.actions[0].kind).toBe("upload"); + expect(act().reason).toBe("file-requires-upload-rpc"); + const fresh = parseObservation(observe()); + const prepared = pageControl("prepare-file", origin, fresh.snapshot, "0"); + expect(prepared.status).toBe("prepared"); expect(pageControl("validate-file", origin, prepared.fileToken!).status).toBe("validated"); + input.files = { 0: { name: "invoice.pdf", size: 12, type: "application/pdf" }, length: 1 }; + expect(pageControl("verify-file", origin, prepared.fileToken!, "", JSON.stringify({ name: "invoice.pdf", size: 12 })).status).toBe("attached"); + expect(pageControl("verify-file", origin, prepared.fileToken!, "", JSON.stringify({ name: "invoice.pdf", size: 12 })).status).toBe("not-executed"); +}); + +it("returns unknown when prepared file metadata does not match", () => { + const input = new Input(); input.type = "file"; input.setAttribute("aria-label", "Upload PDF"); doc.body.append(input); + const snapshot = parseObservation(observe()); const prepared = pageControl("prepare-file", origin, snapshot.snapshot, "0"); + input.files = { 0: { name: "other.pdf", size: 12, type: "application/pdf" }, length: 1 }; + expect(pageControl("verify-file", origin, prepared.fileToken!, "", JSON.stringify({ name: "invoice.pdf", size: 12 }))).toEqual({ status: "unknown", retry: false }); +}); + +it("rechecks privacy before file metadata readback and token-binds cleanup", () => { + const input = new Input(), password = new Input(); input.type = "file"; input.setAttribute("aria-label", "Upload PDF"); password.type = "password"; doc.body.append(input, password); + const snapshot = parseObservation(observe()); const prepared = pageControl("prepare-file", origin, snapshot.snapshot, "0"); + password.value = "PRIVATE_CANARY"; + const verified = pageControl("verify-file", origin, prepared.fileToken!, "", JSON.stringify({ name: "invoice.pdf", size: 12 })); + expect(verified.reason).toBe("populated-private-input"); expect(JSON.stringify(verified)).not.toContain("CANARY"); + expect(pageControl("cancel-file", origin, "wrong").reason).toBe("file-cancel-refused"); + expect(pageControl("cancel-file", origin, prepared.fileToken!).status).toBe("cancelled"); +}); + +it.each(["option", "menuitem", "menuitemcheckbox", "menuitemradio", "checkbox", "radio"])("observes and executes an ARIA %s", role => { + const item = new Element("div", `Select ${role}`); item.setAttribute("role", role); doc.body.append(item); + const result = parseObservation(observe()); + expect(result.actions).toEqual([expect.objectContaining({ id: "0", kind: "click", label: `Select ${role}`, role })]); + expect(act().status).toBe("executed"); + expect(item.clicks).toBe(1); +}); + +it("labels and clicks the unique caret beside a custom combobox", () => { + const fieldLabel = new Element("label", "Rule"), current = new Element("span", "Deny"); + fieldLabel.setAttribute("id", "rule-label"); current.setAttribute("id", "rule-value"); + const combobox = new Element("div"); + combobox.setAttribute("role", "combobox"); + combobox.setAttribute("aria-labelledby", "rule-label rule-value"); + combobox.setAttribute("aria-expanded", "false"); + combobox.append(current); + const caret = new Button(""); + const wrapper = new Element("div"); wrapper.append(combobox, caret); + doc.body.append(fieldLabel, wrapper); + const result = parseObservation(observe()); + expect(result.actions).toEqual([ + expect.objectContaining({ id: "0", kind: "click", label: "Rule Deny options", role: "button" }) + ]); + expect(act("0").status).toBe("executed"); + expect(combobox.clicks).toBe(0); + expect(caret.clicks).toBe(1); +}); + +it("refuses a custom caret action after its adjacent value changes", () => { + const combobox = new Element("div", "Deny"), caret = new Button(""); + combobox.setAttribute("role", "combobox"); + const wrapper = new Element("div"); wrapper.append(combobox, caret); doc.body.append(wrapper); + observe(); + combobox.append(new Text("Allow")); + expect(act().status).toBe("not-executed"); + expect(caret.clicks).toBe(0); +}); + +it("leaves an anonymous caret unnamed when its combobox sibling is ambiguous", () => { + const wrapper = new Element("div"), first = new Element("div", "Rule"), second = new Element("div", "Other"), caret = new Button(""); + first.setAttribute("role", "combobox"); second.setAttribute("role", "combobox"); + wrapper.append(first, second, caret); doc.body.append(wrapper); + expect(parseObservation(observe()).actions[0].label).toBe(""); +}); + +it("exposes Direct's stay-signed-in role checkbox before private transfer", () => { + const checkbox = new Element("div", "Stay signed in for 2 weeks"); checkbox.setAttribute("role", "checkbox"); checkbox.setAttribute("aria-checked", "false"); doc.body.append(checkbox); + const result = parseObservation(observe()); + expect(result.actions).toEqual([expect.objectContaining({ id: "0", kind: "click", label: "Stay signed in for 2 weeks", role: "checkbox" })]); + expect(act().status).toBe("executed"); + expect(checkbox.clicks).toBe(1); +}); + +it.each(["checkbox", "radio"])("observes and executes a native %s input", type => { + const item = new Input(); item.type = type; item.setAttribute("aria-label", `Select ${type}`); doc.body.append(item); + const result = parseObservation(observe()); + expect(result.actions).toEqual([expect.objectContaining({ id: "0", kind: "click", label: `Select ${type}`, role: "input" })]); + expect(act().status).toBe("executed"); + expect(item.clicks).toBe(1); + expect(item.checked).toBe(true); +}); + +it.each(["aria-selected", "checked", "indeterminate"])("invalidates changed fallback-control %s state", state => { + const item = state === "aria-selected" ? new Element("div", "Option") : new Input(); + if (state === "aria-selected") { item.setAttribute("role", "option"); item.setAttribute("aria-selected", "false"); } + else { (item as Input).type = "checkbox"; item.setAttribute("aria-label", "Check"); } + doc.body.append(item); observe(); + if (state === "aria-selected") item.setAttribute("aria-selected", "true"); + else (item as Input)[state] = true; + expect(act().status).toBe("not-executed"); + expect(item.clicks).toBe(0); +}); + it.each(["closed", "object", "nested-open"])("keeps global light-root private checks outside the output scope: %s", surface => { const host = new Element(surface === "object" ? "object" : "x-component"), input = new Input(); input.type = "password"; input.value = "PRIVATE_CANARY"; host.append(input, new Text("OPAQUE_CANARY")); @@ -122,6 +236,90 @@ it("keeps only the prepared single-use private submit exception", () => { expect(pageControl("submit", origin, prepared.submitToken!).status).toBe("not-executed"); expect(button.clicks).toBe(1); }); +const privateSubmit = (disabled = false) => { + const form = new Form(), button = new Button(); button.type = "submit"; button.form = form; form.append(button); doc.body.append(form); + if (disabled) button.setAttribute("disabled", ""); + Object.assign(button, { formMethod: "", formEnctype: "", formNoValidate: false }); Object.assign(form, { enctype: "application/x-www-form-urlencoded", noValidate: false }); + observe(); const prepared = pageControl("prepare-submit", origin, "fresh", "0"); + vi.stubGlobal("__opzeroPrivateDocument", true); vi.stubGlobal("__opzeroPrivateForms", new Set([form])); + return { form, button, prepared }; +}; + +it.each([ + ["submitter formMethod", "button", { formMethod: "get" }], ["submitter formEnctype", "button", { formEnctype: "text/plain" }], + ["submitter formNoValidate", "button", { formNoValidate: true }], ["form method", "form", { method: "get" }], + ["form enctype", "form", { enctype: "multipart/form-data" }], ["form noValidate", "form", { noValidate: true }] +] as const)("refuses a prepared private submit after its %s changes", (_, owner, change) => { + const { form, button, prepared } = privateSubmit(); + expect(prepared.status).toBe("prepared"); + Object.assign(owner === "button" ? button : form, change); + expect(pageControl("submit", origin, prepared.submitToken!)).toMatchObject({ status: "not-executed" }); expect(button.clicks).toBe(0); +}); + +it.each([[60000, "executed"], [90001, "not-executed"]] as const)("bounds a prepared private submit across the credential read: %i ms", (elapsed, status) => { + vi.useFakeTimers(); + const { button, prepared } = privateSubmit(); + expect(prepared).toMatchObject({ status: "prepared", expiresInMs: 90000 }); + vi.advanceTimersByTime(elapsed); + expect(pageControl("submit", origin, prepared.submitToken!)).toMatchObject({ status }); expect(button.clicks).toBe(status === "executed" ? 1 : 0); +}); + +it.each(["enabled-later", "still-disabled", "changed-while-pending"])("refuses a disabled private submit synchronously without replay: %s", async outcome => { + vi.useFakeTimers(); + const { form, button, prepared } = privateSubmit(true); + setTimeout(() => { if (outcome === "enabled-later") button.removeAttribute("disabled"); else if (outcome === "changed-while-pending") form.action = "https://synthetic.invalid/other"; }, 300); + const submitted = pageControl("submit", origin, prepared.submitToken!); + expect(submitted).toMatchObject({ status: "not-executed" }); + await vi.advanceTimersByTimeAsync(2500); + expect(button.clicks).toBe(0); + expect(pageControl("submit", origin, prepared.submitToken!)).toMatchObject({ status: "not-executed" }); expect(button.clicks).toBe(0); +}); + +it.each(["password", "otp", "restored", "nested", "closed"])("blocks capture for a populated %s control inside a shadow root without returning it", mode => { + const host = new Element("x-login"), input = new Input(); input.setAttribute("id", "private"); input.value = "PRIVATE_CANARY"; + if (mode === "otp") input.autocomplete = "one-time-code"; else if (mode !== "restored") input.type = "password"; + const root = host.attachShadow(mode === "closed" ? "closed" : "open"); + if (mode === "nested") { const inner = new Element("x-inner"); root.append(inner); inner.attachShadow("open").append(input); } else root.append(input); + doc.body.append(host, new Button()); + const result = pageControl("capture-check", origin, "", "", JSON.stringify(mode === "restored" ? ["#private"] : [])); + expect(result).toEqual({ status: "checked", allowed: false, reason: mode === "restored" ? "restored-private-selector" : "populated-private-input" }); + expect(JSON.stringify(result)).not.toContain("CANARY"); +}); + +it("allows capture beside a disabled display-only password mask inside a shadow root", () => { + const host = new Element("x-settings"), mask = new Input(); mask.type = "password"; mask.value = "••••••••"; Object.assign(mask, { disabled: true, defaultValue: "••••••••" }); + host.attachShadow("open").append(mask); doc.body.append(host); + expect(pageControl("capture-check", origin, "")).toEqual({ status: "checked", allowed: true }); + mask.value = "PRIVATE_CANARY"; + expect(pageControl("capture-check", origin, "")).toEqual({ status: "checked", allowed: false, reason: "populated-private-input" }); +}); + +it("fails capture closed without the shadow-root detection primitive", () => { + vi.stubGlobal("chrome", {}); + expect(pageControl("capture-check", origin, "")).toEqual({ status: "checked", allowed: false, reason: "unsupported-shadow-root" }); +}); + +it("refuses a disabled submit in the serialized built bundle without a delayed click", async () => { + const b = await background(); + const owner = { session_id: "owner", turn_id: "turn", tabId: 1, expectedOrigin: origin }; + await b.rpc("createTab", owner); await b.rpc("bindPage", owner); + const form = new Form(), button = new Button(); button.type = "submit"; button.form = form; button.setAttribute("disabled", ""); form.append(button); doc.body.append(form); + b.chrome.scripting.executeScript.mockImplementation((request: any, cb: Function) => { + const injected = new Function(`return (${request.func.toString()})`)(); + void Promise.resolve(injected(...request.args)).then(result => cb([{ documentId: "doc-1", frameId: 0, result }])); + }); + const observed = parseObservation((await b.rpc("observePage", owner)).result); + const prepared = (await b.rpc("preparePrivateSubmit", { ...owner, snapshot: observed.snapshot, actionId: "0" })).result; + expect(prepared).toMatchObject({ status: "prepared", expiresInMs: 90000, documentId: "doc-1" }); + vi.stubGlobal("__opzeroPrivateDocument", true); vi.stubGlobal("__opzeroPrivateForms", new Set([form])); + setTimeout(() => button.removeAttribute("disabled"), 200); + expect((await b.rpc("submitPrivate", { ...owner, submitToken: prepared.submitToken, documentId: prepared.documentId })).result).toMatchObject({ status: "not-executed" }); + expect(button.clicks).toBe(0); + await new Promise(resolve => setTimeout(resolve, 400)); + expect(button.clicks).toBe(0); + expect((await b.rpc("submitPrivate", { ...owner, submitToken: prepared.submitToken, documentId: prepared.documentId })).result).toMatchObject({ status: "not-executed" }); +}); + it("reports controls-only and all output bounds without treating labels as private-safe", () => { doc.body.append(new Element("p", "BODY_CANARY " + "a".repeat(13000))); for (let i = 0; i < 101; i++) doc.body.append(new Button("L".repeat(170)), new Element("iframe")); diff --git a/tests/support/background.ts b/tests/support/background.ts index f3f2785..27305de 100644 --- a/tests/support/background.ts +++ b/tests/support/background.ts @@ -14,6 +14,7 @@ export async function background() { let nextTab = 1; const port = { onMessage: event(), onDisconnect: event(), postMessage: (msg: any) => responses.push(msg), disconnect: vi.fn() }; const storage: Record = {}; + let group = { id: 1, title: "", collapsed: false }; const chrome = { runtime: { connectNative: () => port, getManifest: () => ({ version: "test" }), id: "test", onMessage: event(), onStartup: event(), onInstalled: event(), onUpdateAvailable: event(), reload: vi.fn() }, storage: { local: { get: (key: string, cb: Function) => cb({ [key]: storage[key] }), set: (value: object, cb: Function) => { Object.assign(storage, value); cb(); } }, session: { get: (_: unknown, cb: Function) => cb({}) } }, @@ -27,7 +28,10 @@ export async function background() { onRemoved: event(), onReplaced: event(), onUpdated: event() }, history: { search: vi.fn((_: unknown, cb: Function) => cb([])) }, - tabGroups: { get: (_: unknown, cb: Function) => cb({}), update: (_: unknown, __: unknown, cb: Function) => cb({}) }, + tabGroups: { + get: vi.fn((_: unknown, cb: Function) => cb({ ...group })), + update: vi.fn((_: unknown, value: object, cb: Function) => { group = { ...group, ...value }; cb({ ...group }); }) + }, debugger: { attach: vi.fn((_: unknown, __: unknown, cb: Function) => cb()), detach: (_: unknown, cb: Function) => cb(), getTargets: (cb: Function) => cb([{ tabId: 1 }, { tabId: 2 }]), sendCommand: vi.fn((_: unknown, __: unknown, ___: unknown, cb: Function) => cb({})), onEvent: event(), onDetach: event() }, downloads: { onCreated: event(), onChanged: event() }, scripting: { executeScript: vi.fn((_: unknown, cb: Function) => cb([{ documentId: "doc-1", frameId: 0, result: { status: "observed", origin: "https://synthetic.invalid", url: "https://synthetic.invalid/" } }])) } diff --git a/tests/support/scoped-dom.ts b/tests/support/scoped-dom.ts index 9f94523..21b75e2 100644 --- a/tests/support/scoped-dom.ts +++ b/tests/support/scoped-dom.ts @@ -31,16 +31,21 @@ export class Element extends Node { style = { display: "block", visibility: "visible", opacity: "1" }; clicks = 0; modal = false; + listeners = new Map>(); constructor(public localName: string, text = "") { super(); if (text) this.append(new Text(text)); } get shadowRoot() { return this.root?.mode === "open" ? this.root : null; } attachShadow(mode: "open" | "closed") { return this.root = new ShadowRoot(this, mode); } getAttribute(name: string) { return this.attrs.get(name) ?? null; } setAttribute(name: string, value: string) { this.attrs.set(name, value); } + removeAttribute(name: string) { this.attrs.delete(name); } hasAttribute(name: string) { return this.attrs.has(name); } checkVisibility() { return this.style.display !== "none"; } - matches(selector: string): boolean { return selector.split(",").some(s => s === ":modal" ? this.modal : s === ":disabled" ? this.hasAttribute("disabled") : s.startsWith("#") ? this.getAttribute("id") === s.slice(1) : ["[role=button]", "[role=option]", "[role=menuitem]", "[role=menuitemcheckbox]", "[role=menuitemradio]"].includes(s) ? this.getAttribute("role") === s.slice(6, -1) : s === "a[href]" ? this.localName === "a" && this.hasAttribute("href") : s === "head > title" ? this.localName === "title" && this.parentElement?.localName === "head" : this.localName === s); } + getBoundingClientRect() { return { width: 20, height: 20 }; } + matches(selector: string): boolean { return selector.split(",").some(s => s === ":modal" ? this.modal : s === ":disabled" ? this.hasAttribute("disabled") : s.startsWith("#") ? this.getAttribute("id") === s.slice(1) : ["[role=button]", "[role=option]", "[role=menuitem]", "[role=menuitemcheckbox]", "[role=menuitemradio]", "[role=checkbox]", "[role=radio]"].includes(s) ? this.getAttribute("role") === s.slice(6, -1) : s === "a[href]" ? this.localName === "a" && this.hasAttribute("href") : s === "head > title" ? this.localName === "title" && this.parentElement?.localName === "head" : this.localName === s); } + addEventListener(type: string, listener: EventListener) { const listeners = this.listeners.get(type) ?? new Set(); listeners.add(listener); this.listeners.set(type, listeners); } + removeEventListener(type: string, listener: EventListener) { this.listeners.get(type)?.delete(listener); } click() { this.clicks++; } - dispatchEvent(_event: Event) { return true; } + dispatchEvent(event: Event) { for (const listener of this.listeners.get(event.type) ?? []) listener.call(this as unknown as EventTarget, event); return true; } } export class Input extends Element { constructor() { super("input"); } @@ -51,10 +56,19 @@ export class Input extends Element { set type(value: string) { this.setAttribute("type", value); } autocomplete = ""; readOnly = false; + checked = false; + indeterminate = false; labels: Element[] = []; + files: { length: number; [index: number]: { name: string; size: number; type: string } } | null = null; form: Form | null = null; formAction = ""; formTarget = ""; + override click() { + super.click(); + if (this.type === "checkbox") this.checked = !this.checked; + if (this.type === "radio") this.checked = true; + if (this.type === "checkbox" || this.type === "radio") this.indeterminate = false; + } } export class Textarea extends Element { constructor() { super("textarea"); } From 04bdcf2f502314641b1d525831eac3a0eb246eb9 Mon Sep 17 00:00:00 2001 From: afif Date: Mon, 28 Sep 2026 13:43:21 +0800 Subject: [PATCH 04/17] Regenerate installable skill artifacts from a clean node_modules path The previous chunks embedded a machine-local node_modules path in source-map region comments. --- .../{Layer-nAKmzBoW.js => Layer-Dc3MJVHo.js} | 182 +++++++++--------- ...VazUC0E.js => effect-services-DcZl9PNJ.js} | 10 +- .../{rpc-BTiTvO3J.js => rpc-CKph8efs.js} | 28 +-- skills/chrome-control/native-host/client.js | 4 +- skills/chrome-control/native-host/host.js | 4 +- .../chrome-control/native-host/transport.js | 4 +- .../scripts/check-extension-installed.js | 4 +- .../scripts/check-native-host-manifest.js | 4 +- .../scripts/chrome-is-running.js | 4 +- .../scripts/install-native-host.js | 4 +- .../scripts/installed-browsers.js | 4 +- .../scripts/open-chrome-window.js | 4 +- 12 files changed, 128 insertions(+), 128 deletions(-) rename skills/chrome-control/chunks/{Layer-nAKmzBoW.js => Layer-Dc3MJVHo.js} (96%) rename skills/chrome-control/chunks/{effect-services-BVazUC0E.js => effect-services-DcZl9PNJ.js} (85%) rename skills/chrome-control/chunks/{rpc-BTiTvO3J.js => rpc-CKph8efs.js} (98%) diff --git a/skills/chrome-control/chunks/Layer-nAKmzBoW.js b/skills/chrome-control/chunks/Layer-Dc3MJVHo.js similarity index 96% rename from skills/chrome-control/chunks/Layer-nAKmzBoW.js rename to skills/chrome-control/chunks/Layer-Dc3MJVHo.js index 33f3069..62a24f5 100644 --- a/skills/chrome-control/chunks/Layer-nAKmzBoW.js +++ b/skills/chrome-control/chunks/Layer-Dc3MJVHo.js @@ -20,7 +20,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge enumerable: true }) : target, mod)); //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Function.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Function.js /** * Tests if a value is a `function`. * @@ -251,7 +251,7 @@ function pipe(a, ab, bc, cd, de, ef, fg, gh, hi) { } } //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Equivalence.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Equivalence.js /** * This module provides an implementation of the `Equivalence` type class, which defines a binary relation * that is reflexive, symmetric, and transitive. In other words, it defines a notion of equivalence between values of a certain type. @@ -299,7 +299,7 @@ var array$1 = (item) => make$24((self, that) => { return true; }); //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/GlobalValue.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/GlobalValue.js /** * The `GlobalValue` module ensures that a single instance of a value is created globally, * even when modules are imported multiple times (e.g., due to mixing CommonJS and ESM builds) @@ -348,7 +348,7 @@ var globalValue = (id, compute) => { return globalStore.get(id); }; //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Predicate.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Predicate.js /** * This module provides a collection of functions for working with predicates and refinements. * @@ -758,14 +758,14 @@ var isRecord = (input) => isRecordOrArray(input) && !Array.isArray(input); */ var isPromiseLike = (input) => hasProperty(input, "then") && isFunction(input.then); //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/errors.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/errors.js /** * @since 2.0.0 */ /** @internal */ var getBugErrorMessage = (message) => `BUG: ${message} - please report an issue at https://github.com/Effect-TS/effect/issues`; //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Utils.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Utils.js /** * @category constructors * @since 2.0.0 @@ -993,7 +993,7 @@ var internalCall = /* @__PURE__ */ standard.effect_internal_function(() => (/* @ } }.effect_internal_function; (function* () {}).constructor; //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Hash.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Hash.js /** * @since 2.0.0 */ @@ -1122,7 +1122,7 @@ var cached = function() { return hash; }; //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Equal.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Equal.js /** * @since 2.0.0 * @category symbols @@ -1173,7 +1173,7 @@ var isEqual = (u) => hasProperty(u, symbol); */ var equivalence = () => equals$1; //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Inspectable.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Inspectable.js /** * @since 2.0.0 * @category symbols @@ -1319,7 +1319,7 @@ var redact = (u) => { return u; }; //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Pipeable.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Pipeable.js /** * @since 2.0.0 */ @@ -1346,7 +1346,7 @@ var pipeArguments = (self, args) => { } }; //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/opCodes/effect.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/opCodes/effect.js /** @internal */ var OP_ASYNC = "Async"; /** @internal */ @@ -1376,11 +1376,11 @@ var OP_YIELD = "Yield"; /** @internal */ var OP_REVERT_FLAGS = "RevertFlags"; //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/version.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/version.js var moduleVersion = "3.21.2"; var getCurrentVersion = () => moduleVersion; //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/effectable.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/effectable.js /** @internal */ var EffectTypeId$1 = /* @__PURE__ */ Symbol.for("effect/Effect"); /** @internal */ @@ -1476,7 +1476,7 @@ var Base$1 = /* @__PURE__ */ function() { return Base; }(); //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/option.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/option.js /** * @since 2.0.0 */ @@ -1540,7 +1540,7 @@ var some$1 = (value) => { return a; }; //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/either.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/either.js /** * @since 2.0.0 */ @@ -1613,7 +1613,7 @@ var right$1 = (right) => { /** @internal */ var fromOption$1 = /* @__PURE__ */ dual(2, (self, onNone) => isNone$1(self) ? left$1(onNone()) : right$1(self.value)); //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Either.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Either.js /** * Constructs a new `Either` holding a `Right` value. This usually represents a successful value due to the right bias * of this structure. @@ -1801,14 +1801,14 @@ var getOrThrowWith$1 = /* @__PURE__ */ dual(2, (self, onLeft) => { */ var getOrThrow = /* @__PURE__ */ getOrThrowWith$1(() => /* @__PURE__ */ new Error("getOrThrow called on a Left")); //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/array.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/array.js /** * @since 2.0.0 */ /** @internal */ var isNonEmptyArray$1 = (self) => self.length > 0; //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Order.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Order.js /** * This module provides an implementation of the `Order` type class which is used to define a total ordering on some type `A`. * An order is defined by a relation `<=`, which obeys the following laws: @@ -1849,7 +1849,7 @@ var mapInput = /* @__PURE__ */ dual(2, (self, f) => make$23((b1, b2) => self(f(b */ var greaterThan$1 = (O) => dual(2, (self, that) => O(self, that) === 1); //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Option.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Option.js /** * Represents the absence of a value by creating an empty `Option`. * @@ -2564,7 +2564,7 @@ var contains = /* @__PURE__ */ containsWith(/* @__PURE__ */ equivalence()); */ var exists = /* @__PURE__ */ dual(2, (self, refinement) => isNone(self) ? false : refinement(self.value)); //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Tuple.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Tuple.js /** * Constructs a new tuple from the provided values. * @@ -2581,7 +2581,7 @@ var exists = /* @__PURE__ */ dual(2, (self, refinement) => isNone(self) ? false */ var make$22 = (...elements) => elements; //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Array.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Array.js /** * Creates a new `Array` of the specified length. * @@ -3270,7 +3270,7 @@ var dedupe = (self) => dedupeWith(self, equivalence()); */ var join$1 = /* @__PURE__ */ dual(2, (self, sep) => fromIterable$6(self).join(sep)); //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Number.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Number.js /** * @memberof Number * @since 2.0.0 @@ -3295,7 +3295,7 @@ var parse = (s) => { return Number.isNaN(n) ? none$5 : some$1(n); }; //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/RegExp.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/RegExp.js /** * Escapes special characters in a regular expression pattern. * @@ -3311,7 +3311,7 @@ var parse = (s) => { */ var escape = (string) => string.replace(/[/\\^$*+?.()|[\]{}]/g, "\\$&"); //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Boolean.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Boolean.js /** * Negates the given boolean: `!self` * @@ -3329,7 +3329,7 @@ var escape = (string) => string.replace(/[/\\^$*+?.()|[\]{}]/g, "\\$&"); */ var not = (self) => !self; //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/context.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/context.js /** @internal */ var TagTypeId = /* @__PURE__ */ Symbol.for("effect/Context/Tag"); /** @internal */ @@ -3524,7 +3524,7 @@ var mergeAll$2 = (...ctxs) => { return makeContext(map); }; //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Context.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Context.js /** * Creates a new `Tag` instance with an optional key parameter. * @@ -3823,7 +3823,7 @@ var Tag = Tag$1; */ var Reference = Reference$1; //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Chunk.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Chunk.js /** * @since 2.0.0 */ @@ -4235,7 +4235,7 @@ var headNonEmpty = unsafeHead; */ var tailNonEmpty = (self) => drop(self, 1); //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Duration.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Duration.js /** * @since 2.0.0 */ @@ -4591,7 +4591,7 @@ var MAX_INDEX_NODE = BUCKET_SIZE / 2; /** @internal */ var MIN_ARRAY_NODE = BUCKET_SIZE / 4; //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/hashMap/bitwise.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/hashMap/bitwise.js /** * Hamming weight. * @@ -4620,13 +4620,13 @@ function fromBitmap(bitmap, bit) { return popcount(bitmap & bit - 1); } //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/stack.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/stack.js var make$17 = (value, previous) => ({ value, previous }); //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/hashMap/array.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/hashMap/array.js /** @internal */ function arrayUpdate(mutate, at, v, arr) { let out = arr; @@ -4671,7 +4671,7 @@ function arraySpliceIn(mutate, at, v, arr) { return out; } //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/hashMap/node.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/hashMap/node.js /** @internal */ var EmptyNode = class EmptyNode { _tag = "EmptyNode"; @@ -4903,7 +4903,7 @@ function mergeLeaves(edit, shift, h1, n1, h2, n2) { } } //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/hashMap.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/hashMap.js var HashMapSymbolKey = "effect/HashMap"; /** @internal */ var HashMapTypeId = /* @__PURE__ */ Symbol.for(HashMapSymbolKey); @@ -5116,7 +5116,7 @@ var reduce$5 = /* @__PURE__ */ dual(3, (self, zero, f) => { return zero; }); //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/hashSet.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/hashSet.js var HashSetSymbolKey = "effect/HashSet"; /** @internal */ var HashSetTypeId = /* @__PURE__ */ Symbol.for(HashSetSymbolKey); @@ -5206,7 +5206,7 @@ var forEach$2 = /* @__PURE__ */ dual(2, (self, f) => forEach$3(self._keyMap, (_, /** @internal */ var reduce$4 = /* @__PURE__ */ dual(3, (self, zero, f) => reduce$5(self._keyMap, zero, (z, _, a) => f(z, a))); //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/HashSet.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/HashSet.js /** * # HashSet * @@ -5872,7 +5872,7 @@ var union = union$1; */ var reduce$3 = reduce$4; //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/MutableRef.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/MutableRef.js var TypeId$6 = /* @__PURE__ */ Symbol.for("effect/MutableRef"); var MutableRefProto = { [TypeId$6]: TypeId$6, @@ -5915,7 +5915,7 @@ var set$3 = /* @__PURE__ */ dual(2, (self, value) => { return self; }); //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/fiberId.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/fiberId.js /** @internal */ var FiberIdSymbolKey = "effect/FiberId"; /** @internal */ @@ -6047,7 +6047,7 @@ var unsafeMake$5 = () => { return new Runtime(id, Date.now()); }; //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/FiberId.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/FiberId.js /** * @since 2.0.0 * @category constructors @@ -6087,7 +6087,7 @@ var threadName = threadName$1; */ var unsafeMake$4 = unsafeMake$5; //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/HashMap.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/HashMap.js /** * @since 2.0.0 */ @@ -6167,7 +6167,7 @@ var map$2 = map$3; */ var reduce$2 = reduce$5; //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/List.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/List.js /** * A data type for immutable linked lists representing ordered collections of elements of type `A`. * @@ -6452,7 +6452,7 @@ var Structural = /* @__PURE__ */ function() { return Structural; }(); //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/differ/contextPatch.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/differ/contextPatch.js /** @internal */ var ContextPatchTypeId = /* @__PURE__ */ Symbol.for("effect/DifferContextPatch"); function variance$3(a) { @@ -6556,7 +6556,7 @@ var patch$7 = /* @__PURE__ */ dual(2, (self, context) => { return makeContext(map); }); //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/differ/hashSetPatch.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/differ/hashSetPatch.js /** @internal */ var HashSetPatchTypeId = /* @__PURE__ */ Symbol.for("effect/DifferHashSetPatch"); function variance$2(a) { @@ -6633,7 +6633,7 @@ var patch$6 = /* @__PURE__ */ dual(2, (self, oldValue) => { return set; }); //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/differ/readonlyArrayPatch.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/differ/readonlyArrayPatch.js /** @internal */ var ReadonlyArrayPatchTypeId = /* @__PURE__ */ Symbol.for("effect/DifferReadonlyArrayPatch"); function variance$1(a) { @@ -6788,7 +6788,7 @@ var updateWith = (f) => make$13({ patch: (patch, oldValue) => f(oldValue, patch(oldValue)) }); //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/runtimeFlagsPatch.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/runtimeFlagsPatch.js /** @internal */ var BIT_MASK = 255; /** @internal */ @@ -6840,7 +6840,7 @@ var differ$1 = /* @__PURE__ */ make$13({ patch: (_patch, oldValue) => patch$4(oldValue, _patch) }); //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/RuntimeFlagsPatch.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/RuntimeFlagsPatch.js /** * Creates a `RuntimeFlagsPatch` describing enabling the provided `RuntimeFlag`. * @@ -6864,7 +6864,7 @@ var disable = disable$1; */ var exclude = exclude$1; //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/blockedRequests.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/blockedRequests.js /** * Combines this collection of blocked requests with the specified collection * of blocked requests, in parallel. @@ -7038,7 +7038,7 @@ var OP_PARALLEL$1 = "Parallel"; /** @internal */ var OP_SEQUENTIAL$1 = "Sequential"; //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/cause.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/cause.js /** @internal */ var CauseSymbolKey = "effect/Cause"; /** @internal */ @@ -7533,7 +7533,7 @@ var prettyErrors = (cause) => reduceWithContext(cause, void 0, { sequentialCase: (_, l, r) => [...l, ...r] }); //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/opCodes/deferred.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/opCodes/deferred.js /** @internal */ var OP_STATE_PENDING = "Pending"; /** @internal */ @@ -7562,7 +7562,7 @@ var done$2 = (effect) => { }; }; //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/singleShotGen.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/singleShotGen.js /** @internal */ var SingleShotGen = class SingleShotGen { self; @@ -7593,7 +7593,7 @@ var SingleShotGen = class SingleShotGen { } }; //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/core.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/core.js /** * @internal */ @@ -8432,7 +8432,7 @@ var currentSpanFromFiber = (fiber) => { return span !== void 0 && span._tag === "Span" ? some(span) : none$4(); }; //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Exit.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Exit.js /** * Returns `true` if the specified `Exit` is a `Failure`, `false` otherwise. * @@ -8448,7 +8448,7 @@ var isFailure = exitIsFailure; */ var isSuccess = exitIsSuccess; //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/MutableHashMap.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/MutableHashMap.js var TypeId$4 = /* @__PURE__ */ Symbol.for("effect/MutableHashMap"); var MutableHashMapProto = { [TypeId$4]: TypeId$4, @@ -8758,7 +8758,7 @@ var prefixed = /* @__PURE__ */ dual(2, (self, prefix) => { } }); //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/configProvider/pathPatch.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/configProvider/pathPatch.js /** @internal */ var empty$4 = { _tag: "Empty" }; /** @internal */ @@ -8793,7 +8793,7 @@ var patch$3 = /* @__PURE__ */ dual(2, (path, patch) => { return right(output); }); //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/opCodes/config.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/opCodes/config.js /** @internal */ var OP_CONSTANT = "Constant"; /** @internal */ @@ -8817,7 +8817,7 @@ var OP_HASHMAP = "HashMap"; /** @internal */ var OP_ZIP_WITH = "ZipWith"; //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/configProvider.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/configProvider.js var concat = (l, r) => [...l, ...r]; /** @internal */ var ConfigProviderTypeId = /* @__PURE__ */ Symbol.for("effect/ConfigProvider"); @@ -8975,7 +8975,7 @@ var parseInteger = (str) => { return Number.isNaN(parsedIndex) ? none$4() : some(parsedIndex); }; //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/defaultServices/console.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/defaultServices/console.js /** @internal */ var TypeId$3 = /* @__PURE__ */ Symbol.for("effect/Console"); /** @internal */ @@ -9112,7 +9112,7 @@ var swap = (buffer, index1, index2) => { }; var make$8 = (seed) => new RandomImpl(hash(seed)); //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/tracer.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/tracer.js /** * @since 2.0.0 */ @@ -9196,7 +9196,7 @@ var nativeTracer = /* @__PURE__ */ make$7({ /** @internal */ var DisablePropagation = /* @__PURE__ */ Reference()("effect/Tracer/DisablePropagation", { defaultValue: constFalse }); //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/defaultServices.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/defaultServices.js /** @internal */ var liveServices = /* @__PURE__ */ pipe(/* @__PURE__ */ empty$17(), /* @__PURE__ */ add$2(clockTag, /* @__PURE__ */ make$10()), /* @__PURE__ */ add$2(consoleTag, defaultConsole), /* @__PURE__ */ add$2(randomTag, /* @__PURE__ */ make$8(/* @__PURE__ */ Math.random())), /* @__PURE__ */ add$2(configProviderTag, /* @__PURE__ */ fromEnv()), /* @__PURE__ */ add$2(tracerTag, nativeTracer)); /** @@ -9207,7 +9207,7 @@ var liveServices = /* @__PURE__ */ pipe(/* @__PURE__ */ empty$17(), /* @__PURE__ */ var currentServices = /* @__PURE__ */ globalValue(/* @__PURE__ */ Symbol.for("effect/DefaultServices/currentServices"), () => fiberRefUnsafeMakeContext(liveServices)); //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/fiberRefs.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/fiberRefs.js /** @internal */ function unsafeMake$3(fiberRefLocals) { return new FiberRefsImpl(fiberRefLocals); @@ -9342,7 +9342,7 @@ var updateManyAs$1 = /* @__PURE__ */ dual(2, (self, { entries, forkAs }) => { return new FiberRefsImpl(locals); }); //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/FiberRefs.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/FiberRefs.js /** * Gets the value of the specified `FiberRef` in this collection of `FiberRef` * values if it exists or the `initial` value of the `FiberRef` otherwise. @@ -9373,7 +9373,7 @@ var updateManyAs = updateManyAs$1; */ var empty$2 = empty$3; //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/LogLevel.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/LogLevel.js /** * @since 2.0.0 * @category constructors @@ -9436,7 +9436,7 @@ var fromLiteral = (literal) => { } }; //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/logSpan.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/logSpan.js /** * Sanitize a given string by replacing spaces, equal signs, and double quotes with underscores. * @@ -9448,7 +9448,7 @@ var render = (now) => (self) => { return `${formatLabel(self.label)}=${now - self.startTime}ms`; }; //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Effectable.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Effectable.js /** * @since 2.0.0 * @category prototypes @@ -9461,14 +9461,14 @@ var Base = Base$1; */ var Class = class extends Base {}; //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Readable.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Readable.js /** * @since 2.0.0 * @category type ids */ var TypeId$2 = /* @__PURE__ */ Symbol.for("effect/Readable"); //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/ref.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/ref.js /** @internal */ var RefTypeId = /* @__PURE__ */ Symbol.for("effect/Ref"); /** @internal */ @@ -9510,7 +9510,7 @@ var modify = /* @__PURE__ */ dual(2, (self, f) => self.modify(f)); /** @internal */ var update$1 = /* @__PURE__ */ dual(2, (self, f) => self.modify((a) => [void 0, f(a)])); //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/fiberRefs/patch.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/fiberRefs/patch.js /** @internal */ var OP_EMPTY$1 = "Empty"; /** @internal */ @@ -9595,7 +9595,7 @@ var patch$2 = /* @__PURE__ */ dual(3, (self, fiberId, oldValue) => { return fiberRefs; }); //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/metric/label.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/metric/label.js /** @internal */ var MetricLabelSymbolKey = "effect/MetricLabel"; /** @internal */ @@ -9628,7 +9628,7 @@ var make$5 = (key, value) => { /** @internal */ var isMetricLabel = (u) => hasProperty(u, MetricLabelTypeId); //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/core-effect.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/core-effect.js var asSome = (self) => map$1(self, some); var try_$1 = (arg) => { let evaluate; @@ -9683,7 +9683,7 @@ var updateFiberRefs = (f) => withFiberRuntime((state) => { }); var filterDisablePropagation = /* @__PURE__ */ flatMap$4((span) => get$6(span.context, DisablePropagation) ? span._tag === "Span" ? filterDisablePropagation(span.parent) : none$4() : some(span)); //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/executionStrategy.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/executionStrategy.js /** @internal */ var OP_SEQUENTIAL = "Sequential"; /** @internal */ @@ -9704,7 +9704,7 @@ var isSequential = (self) => self._tag === OP_SEQUENTIAL; /** @internal */ var isParallel = (self) => self._tag === OP_PARALLEL; //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/ExecutionStrategy.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/ExecutionStrategy.js /** * Execute effects sequentially. * @@ -9727,7 +9727,7 @@ var parallel = parallel$1; */ var parallelN = parallelN$1; //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/FiberRefsPatch.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/FiberRefsPatch.js /** * Constructs a patch that describes the changes between the specified * collections of `FiberRef` @@ -9745,7 +9745,7 @@ var diff$1 = diff$2; */ var patch$1 = patch$2; //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/fiberStatus.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/fiberStatus.js var FiberStatusSymbolKey = "effect/FiberStatus"; /** @internal */ var FiberStatusTypeId = /* @__PURE__ */ Symbol.for(FiberStatusSymbolKey); @@ -9810,7 +9810,7 @@ var isFiberStatus = (u) => hasProperty(u, FiberStatusTypeId); /** @internal */ var isDone$1 = (self) => self._tag === OP_DONE; //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/FiberStatus.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/FiberStatus.js /** * @since 2.0.0 * @category constructors @@ -9834,7 +9834,7 @@ var suspended = suspended$1; */ var isDone = isDone$1; //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Micro.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Micro.js /** * @since 3.4.0 * @experimental @@ -10511,7 +10511,7 @@ var TaggedError = (tag) => { TaggedError("NoSuchElementException"); TaggedError("TimeoutException"); //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Scheduler.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Scheduler.js /** * @since 3.20.0 * @category models @@ -10653,11 +10653,11 @@ var SyncScheduler = class { /** @internal */ var currentScheduler = /* @__PURE__ */ globalValue(/* @__PURE__ */ Symbol.for("effect/FiberRef/currentScheduler"), () => fiberRefUnsafeMake(defaultScheduler)); //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/completedRequestMap.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/completedRequestMap.js /** @internal */ var currentRequestMap = /* @__PURE__ */ globalValue(/* @__PURE__ */ Symbol.for("effect/FiberRef/currentRequestMap"), () => fiberRefUnsafeMake(/* @__PURE__ */ new Map())); //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/concurrency.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/concurrency.js /** @internal */ var match = (concurrency, sequential, unbounded, bounded) => { switch (concurrency) { @@ -10668,7 +10668,7 @@ var match = (concurrency, sequential, unbounded, bounded) => { } }; //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/fiberMessage.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/fiberMessage.js /** @internal */ var OP_INTERRUPT_SIGNAL = "InterruptSignal"; /** @internal */ @@ -10934,7 +10934,7 @@ var prettyLoggerBrowser = (options) => { /** @internal */ var prettyLoggerDefault = /* @__PURE__ */ globalValue("effect/Logger/prettyLoggerDefault", () => prettyLogger()); //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/metric/boundaries.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/metric/boundaries.js /** @internal */ var MetricBoundariesSymbolKey = "effect/MetricBoundaries"; /** @internal */ @@ -11794,7 +11794,7 @@ var fromEffect = (effect) => { /** @internal */ var none = /* @__PURE__ */ globalValue("effect/Supervisor/none", () => fromEffect(void_$1)); //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Differ.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Differ.js /** * Constructs a new `Differ`. * @@ -11803,7 +11803,7 @@ var none = /* @__PURE__ */ globalValue("effect/Supervisor/none", () => fromEffec */ var make$1 = make$13; //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/supervisor/patch.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/supervisor/patch.js /** @internal */ var OP_EMPTY = "Empty"; /** @internal */ @@ -11897,7 +11897,7 @@ var differ = /* @__PURE__ */ make$1({ diff }); //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/fiberRuntime.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/fiberRuntime.js /** @internal */ var fiberStarted = /* @__PURE__ */ counter("effect_fiber_started", { incremental: true }); /** @internal */ @@ -13240,7 +13240,7 @@ var invokeWithInterrupt = (self, entries, onInterrupt) => fiberIdWith((id) => en }), (entry) => complete(entry.request, exitInterrupt$1(id))); }))); //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Cause.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Cause.js /** * Checks if a `Cause` is a `Fail` type. * @@ -13296,7 +13296,7 @@ var IllegalArgumentException = IllegalArgumentException$1; */ var pretty = pretty$1; //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Scope.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Scope.js /** * @since 2.0.0 */ @@ -13317,7 +13317,7 @@ var close = scopeClose; */ var fork = scopeFork; //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/effect/circular.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/effect/circular.js /** @internal */ var Semaphore = class { permits; @@ -13419,11 +13419,11 @@ var unsafeMakeSynchronized = (value) => { return new SynchronizedImpl(unsafeMake$2(value), unsafeMakeSemaphore(1).withPermits(1)); }; //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/managedRuntime/circular.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/managedRuntime/circular.js /** @internal */ var TypeId = /* @__PURE__ */ Symbol.for("effect/ManagedRuntime"); //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/opCodes/layer.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/opCodes/layer.js /** @internal */ var OP_FRESH = "Fresh"; /** @internal */ @@ -13431,7 +13431,7 @@ var OP_FROM_EFFECT = "FromEffect"; /** @internal */ var OP_MERGE_ALL = "MergeAll"; //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/runtime.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/runtime.js var makeDual = (f) => function() { if (arguments.length === 1) { const runtime = arguments[0]; @@ -13570,7 +13570,7 @@ var unsafeForkEffect = /* @__PURE__ */ unsafeFork(defaultRuntime); /** @internal */ var unsafeRunSyncEffect = /* @__PURE__ */ unsafeRunSync(defaultRuntime); //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/synchronizedRef.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/synchronizedRef.js /** @internal */ var modifyEffect = /* @__PURE__ */ dual(2, (self, f) => self.modifyEffect(f)); /** @internal */ @@ -13729,7 +13729,7 @@ var effect_provide = /* @__PURE__ */ dual(2, (self, source) => { else return provideSomeRuntime(self, source); }); //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Effect.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Effect.js /** * Checks if a given value is an `Effect` value. * @@ -14590,7 +14590,7 @@ var runFork = unsafeForkEffect; */ var runSync = unsafeRunSyncEffect; //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Layer.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Layer.js /** * Constructs a layer from the specified value. * diff --git a/skills/chrome-control/chunks/effect-services-BVazUC0E.js b/skills/chrome-control/chunks/effect-services-DcZl9PNJ.js similarity index 85% rename from skills/chrome-control/chunks/effect-services-BVazUC0E.js rename to skills/chrome-control/chunks/effect-services-DcZl9PNJ.js index 1012110..09c2fb0 100644 --- a/skills/chrome-control/chunks/effect-services-BVazUC0E.js +++ b/skills/chrome-control/chunks/effect-services-DcZl9PNJ.js @@ -1,16 +1,16 @@ -const require_Layer = require("./Layer-nAKmzBoW.js"); +const require_Layer = require("./Layer-Dc3MJVHo.js"); let node_fs = require("node:fs"); node_fs = require_Layer.__toESM(node_fs); let node_child_process = require("node:child_process"); node_child_process = require_Layer.__toESM(node_child_process); -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/FiberRef.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/FiberRef.js /** * @since 2.0.0 * @category fiberRefs */ var currentLoggers = require_Layer.currentLoggers; //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Logger.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Logger.js /** * @since 2.0.0 * @category constructors @@ -24,7 +24,7 @@ var defaultLogger = require_Layer.defaultLogger; */ var prettyLoggerDefault = require_Layer.prettyLoggerDefault; //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/@effect+platform@0.96.1_effect@3.21.2/node_modules/@effect/platform/dist/esm/Runtime.js +//#region node_modules/.pnpm/@effect+platform@0.96.1_effect@3.21.2/node_modules/@effect/platform/dist/esm/Runtime.js /** * @since 1.0.0 */ @@ -58,7 +58,7 @@ var makeRunMain = (f) => require_Layer.dual((args) => require_Layer.isEffect(arg }); }); //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/@effect+platform-node@0.106.0_@effect+cluster@0.58.2_@effect+platform@0.96.1_effect@3.2_a80e93d5c9b0cafd3a2a57951ab69ada/node_modules/@effect/platform-node/dist/esm/NodeRuntime.js +//#region node_modules/.pnpm/@effect+platform-node@0.106.0_@effect+cluster@0.58.2_@effect+platform@0.96.1_effect@3.2_a80e93d5c9b0cafd3a2a57951ab69ada/node_modules/@effect/platform-node/dist/esm/NodeRuntime.js /** * @since 1.0.0 */ diff --git a/skills/chrome-control/chunks/rpc-BTiTvO3J.js b/skills/chrome-control/chunks/rpc-CKph8efs.js similarity index 98% rename from skills/chrome-control/chunks/rpc-BTiTvO3J.js rename to skills/chrome-control/chunks/rpc-CKph8efs.js index 66b44ac..7d4806e 100644 --- a/skills/chrome-control/chunks/rpc-BTiTvO3J.js +++ b/skills/chrome-control/chunks/rpc-CKph8efs.js @@ -1,5 +1,5 @@ -const require_Layer = require("./Layer-nAKmzBoW.js"); -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/schema/util.js +const require_Layer = require("./Layer-Dc3MJVHo.js"); +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/schema/util.js /** @internal */ var getKeysForIndexSignature = (input, parameter) => { switch (parameter._tag) { @@ -29,7 +29,7 @@ var formatPathKey = (key) => `[${require_Layer.formatPropertyKey(key)}]`; /** @internal */ var formatPath = (path) => isNonEmpty$1(path) ? path.map(formatPathKey).join("") : formatPathKey(path); //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/schema/errors.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/schema/errors.js var getErrorMessage$1 = (reason, details, path, ast) => { let out = reason; if (path && require_Layer.isNonEmptyReadonlyArray(path)) out += `\nat path: ${formatPath(path)}`; @@ -52,7 +52,7 @@ var getASTDuplicatePropertySignatureTransformationErrorMessage = (key) => getErr /** @internal */ var getASTDuplicatePropertySignatureErrorMessage = (key) => getErrorMessage$1("Duplicate property signature", `Duplicate key ${require_Layer.formatUnknown(key)}`); //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/schema/schemaId.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/schema/schemaId.js /** @internal */ var DateFromSelfSchemaId$1 = /* @__PURE__ */ Symbol.for("effect/SchemaId/DateFromSelf"); /** @internal */ @@ -82,7 +82,7 @@ var MinLengthSchemaId$1 = /* @__PURE__ */ Symbol.for("effect/SchemaId/MinLength" /** @internal */ var LengthSchemaId$1 = /* @__PURE__ */ Symbol.for("effect/SchemaId/Length"); //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/SchemaAST.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/SchemaAST.js /** * @since 3.10.0 */ @@ -1567,7 +1567,7 @@ function getBrands(ast) { var getOrElseExpected = (ast) => getTitleAnnotation(ast).pipe(require_Layer.orElse(() => getDescriptionAnnotation(ast)), require_Layer.orElse(() => getAutoTitleAnnotation(ast)), require_Layer.map$1((s) => s + getBrands(ast))); var getExpected = (ast) => require_Layer.orElse(getIdentifierAnnotation(ast), () => getOrElseExpected(ast)); //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/BigDecimal.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/BigDecimal.js /** * This module provides utility functions and type class instances for working with the `BigDecimal` type in TypeScript. * It includes functions for basic arithmetic operations, as well as type class instances for `Equivalence` and `Order`. @@ -1941,7 +1941,7 @@ var isZero = (n) => n.value === bigint0; */ var isNegative = (n) => n.value < bigint0; //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/BigInt.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/BigInt.js /** * Takes a `bigint` and returns an `Option` of `number`. * @@ -2021,7 +2021,7 @@ var fromNumber = (n) => { } }; //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Data.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Data.js /** * Provides a constructor for a Case Class. * @@ -2061,7 +2061,7 @@ var TaggedError = (tag) => { return O.BaseEffectError; }; //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/dateTime.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/internal/dateTime.js /** @internal */ var TypeId$1 = /* @__PURE__ */ Symbol.for("effect/DateTime"); /** @internal */ @@ -2402,7 +2402,7 @@ var formatIsoOffset = (self) => { /** @internal */ var formatIsoZoned$1 = (self) => self.zone._tag === "Offset" ? formatIsoOffset(self) : `${formatIsoOffset(self)}[${self.zone.id}]`; //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/String.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/String.js /** * @example * ```ts @@ -2464,7 +2464,7 @@ var uncapitalize = (self) => { */ var isNonEmpty = (self) => self.length > 0; //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/DateTime.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/DateTime.js /** * @since 3.6.0 * @category guards @@ -2635,7 +2635,7 @@ var formatIso = formatIso$1; */ var formatIsoZoned = formatIsoZoned$1; //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/ParseResult.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/ParseResult.js /** * @since 3.10.0 */ @@ -3654,7 +3654,7 @@ var formatTree = (issue) => { } }; //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Struct.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Struct.js /** * Create a new object by picking properties of an existing object. * @@ -3694,7 +3694,7 @@ var omit = /* @__PURE__ */ require_Layer.dual((args) => require_Layer.isObject(a return out; }); //#endregion -//#region ../../../browser-platform-personal-capture-20260920/op-chrome/node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Schema.js +//#region node_modules/.pnpm/effect@3.21.2/node_modules/effect/dist/esm/Schema.js /** * @since 3.10.0 */ diff --git a/skills/chrome-control/native-host/client.js b/skills/chrome-control/native-host/client.js index 4f1fee5..7503341 100644 --- a/skills/chrome-control/native-host/client.js +++ b/skills/chrome-control/native-host/client.js @@ -1,6 +1,6 @@ #!/usr/bin/env node -const require_Layer = require("../chunks/Layer-nAKmzBoW.js"); -const require_rpc = require("../chunks/rpc-BTiTvO3J.js"); +const require_Layer = require("../chunks/Layer-Dc3MJVHo.js"); +const require_rpc = require("../chunks/rpc-CKph8efs.js"); let node_fs = require("node:fs"); node_fs = require_Layer.__toESM(node_fs); let node_net = require("node:net"); diff --git a/skills/chrome-control/native-host/host.js b/skills/chrome-control/native-host/host.js index 98c0c85..4fa34a2 100644 --- a/skills/chrome-control/native-host/host.js +++ b/skills/chrome-control/native-host/host.js @@ -1,6 +1,6 @@ #!/usr/bin/env node -const require_Layer = require("../chunks/Layer-nAKmzBoW.js"); -const require_rpc = require("../chunks/rpc-BTiTvO3J.js"); +const require_Layer = require("../chunks/Layer-Dc3MJVHo.js"); +const require_rpc = require("../chunks/rpc-CKph8efs.js"); let node_fs = require("node:fs"); node_fs = require_Layer.__toESM(node_fs); let node_net = require("node:net"); diff --git a/skills/chrome-control/native-host/transport.js b/skills/chrome-control/native-host/transport.js index 439df1b..4a8ab84 100644 --- a/skills/chrome-control/native-host/transport.js +++ b/skills/chrome-control/native-host/transport.js @@ -1,6 +1,6 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" }); -const require_Layer = require("../chunks/Layer-nAKmzBoW.js"); -const require_rpc = require("../chunks/rpc-BTiTvO3J.js"); +const require_Layer = require("../chunks/Layer-Dc3MJVHo.js"); +const require_rpc = require("../chunks/rpc-CKph8efs.js"); let node_net = require("node:net"); node_net = require_Layer.__toESM(node_net); let node_path = require("node:path"); diff --git a/skills/chrome-control/scripts/check-extension-installed.js b/skills/chrome-control/scripts/check-extension-installed.js index 1e6bb21..62a0680 100644 --- a/skills/chrome-control/scripts/check-extension-installed.js +++ b/skills/chrome-control/scripts/check-extension-installed.js @@ -1,6 +1,6 @@ #!/usr/bin/env node -const require_Layer = require("../chunks/Layer-nAKmzBoW.js"); -const require_effect_services = require("../chunks/effect-services-BVazUC0E.js"); +const require_Layer = require("../chunks/Layer-Dc3MJVHo.js"); +const require_effect_services = require("../chunks/effect-services-DcZl9PNJ.js"); let node_os = require("node:os"); node_os = require_Layer.__toESM(node_os); let node_path = require("node:path"); diff --git a/skills/chrome-control/scripts/check-native-host-manifest.js b/skills/chrome-control/scripts/check-native-host-manifest.js index c51fc68..7847fb6 100644 --- a/skills/chrome-control/scripts/check-native-host-manifest.js +++ b/skills/chrome-control/scripts/check-native-host-manifest.js @@ -1,6 +1,6 @@ #!/usr/bin/env node -const require_Layer = require("../chunks/Layer-nAKmzBoW.js"); -const require_effect_services = require("../chunks/effect-services-BVazUC0E.js"); +const require_Layer = require("../chunks/Layer-Dc3MJVHo.js"); +const require_effect_services = require("../chunks/effect-services-DcZl9PNJ.js"); let node_os = require("node:os"); node_os = require_Layer.__toESM(node_os); let node_path = require("node:path"); diff --git a/skills/chrome-control/scripts/chrome-is-running.js b/skills/chrome-control/scripts/chrome-is-running.js index 33ed5a2..2351963 100644 --- a/skills/chrome-control/scripts/chrome-is-running.js +++ b/skills/chrome-control/scripts/chrome-is-running.js @@ -1,6 +1,6 @@ #!/usr/bin/env node -const require_Layer = require("../chunks/Layer-nAKmzBoW.js"); -const require_effect_services = require("../chunks/effect-services-BVazUC0E.js"); +const require_Layer = require("../chunks/Layer-Dc3MJVHo.js"); +const require_effect_services = require("../chunks/effect-services-DcZl9PNJ.js"); //#region src/scripts/chrome-is-running.ts var json = process.argv.includes("--json"); require_effect_services.runScript(require_Layer.gen(function* () { diff --git a/skills/chrome-control/scripts/install-native-host.js b/skills/chrome-control/scripts/install-native-host.js index a9f5027..b9c8824 100644 --- a/skills/chrome-control/scripts/install-native-host.js +++ b/skills/chrome-control/scripts/install-native-host.js @@ -1,6 +1,6 @@ #!/usr/bin/env node -const require_Layer = require("../chunks/Layer-nAKmzBoW.js"); -const require_effect_services = require("../chunks/effect-services-BVazUC0E.js"); +const require_Layer = require("../chunks/Layer-Dc3MJVHo.js"); +const require_effect_services = require("../chunks/effect-services-DcZl9PNJ.js"); let node_os = require("node:os"); node_os = require_Layer.__toESM(node_os); let node_path = require("node:path"); diff --git a/skills/chrome-control/scripts/installed-browsers.js b/skills/chrome-control/scripts/installed-browsers.js index db2d501..844ddda 100644 --- a/skills/chrome-control/scripts/installed-browsers.js +++ b/skills/chrome-control/scripts/installed-browsers.js @@ -1,6 +1,6 @@ #!/usr/bin/env node -const require_Layer = require("../chunks/Layer-nAKmzBoW.js"); -const require_effect_services = require("../chunks/effect-services-BVazUC0E.js"); +const require_Layer = require("../chunks/Layer-Dc3MJVHo.js"); +const require_effect_services = require("../chunks/effect-services-DcZl9PNJ.js"); //#region src/scripts/installed-browsers.ts var json = process.argv.includes("--json"); function commandExists(command) { diff --git a/skills/chrome-control/scripts/open-chrome-window.js b/skills/chrome-control/scripts/open-chrome-window.js index a222c70..e6a29ae 100644 --- a/skills/chrome-control/scripts/open-chrome-window.js +++ b/skills/chrome-control/scripts/open-chrome-window.js @@ -1,6 +1,6 @@ #!/usr/bin/env node -const require_Layer = require("../chunks/Layer-nAKmzBoW.js"); -const require_effect_services = require("../chunks/effect-services-BVazUC0E.js"); +const require_Layer = require("../chunks/Layer-Dc3MJVHo.js"); +const require_effect_services = require("../chunks/effect-services-DcZl9PNJ.js"); //#region src/scripts/open-chrome-window.ts var json = process.argv.includes("--json"); var dryRun = process.argv.includes("--dry-run"); From 9b0fcea558505eb966f50c9df8111fa92a147b74 Mon Sep 17 00:00:00 2001 From: afif Date: Mon, 28 Sep 2026 14:09:01 +0800 Subject: [PATCH 05/17] Rename the extension to Browser Control and bump to 0.2.1 Chrome Web Store branding guidelines bar Google trademarks in an item name. The native host name com.opzero.chrome and protocol v2 stay the same, so the installed fast-chrome controller keeps working. --- .github/workflows/chrome-web-store.yml | 2 +- .github/workflows/release.yml | 2 +- package.json | 4 ++-- scripts/build.mjs | 2 +- .../chrome-control/scripts/check-extension-installed.js | 6 +++--- skills/chrome-control/scripts/install-native-host.js | 2 +- src/extension/background.ts | 4 ++-- src/extension/manifest.json | 8 ++++---- src/extension/popup.html | 6 +++--- src/extension/popup.ts | 2 +- src/scripts/check-extension-installed.ts | 6 +++--- src/scripts/install-native-host.ts | 2 +- tests/acceptance/distribution.test.ts | 8 ++++---- 13 files changed, 27 insertions(+), 27 deletions(-) diff --git a/.github/workflows/chrome-web-store.yml b/.github/workflows/chrome-web-store.yml index 09cc56d..9d78338 100644 --- a/.github/workflows/chrome-web-store.yml +++ b/.github/workflows/chrome-web-store.yml @@ -68,7 +68,7 @@ jobs: set -euo pipefail response="$(curl -sS -w '\n%{http_code}' -X POST \ -H "Authorization: Bearer ${ACCESS_TOKEN}" \ - -T dist/release/opzero-chrome-extension.zip \ + -T dist/release/browser-control-extension.zip \ "https://chromewebstore.googleapis.com/upload/v2/publishers/${CHROME_PUBLISHER_ID}/items/${EXTENSION_ID}:upload")" status="$(printf '%s' "$response" | tail -n 1)" body="$(printf '%s' "$response" | sed '$d')" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e82dccd..86f7cf3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -29,5 +29,5 @@ jobs: uses: softprops/action-gh-release@v2 with: files: | - dist/release/opzero-chrome-extension.zip + dist/release/browser-control-extension.zip dist/release/chrome-control-skill.zip diff --git a/package.json b/package.json index b307b51..19f1033 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,8 @@ { "name": "opzero-chrome", - "version": "0.2.0", + "version": "0.2.1", "private": true, - "description": "Opzero Chrome extension with native messaging and CDP bridge.", + "description": "Browser Control extension with native messaging and CDP bridge.", "packageManager": "pnpm@10.33.2", "scripts": { "build": "node scripts/build.mjs", diff --git a/scripts/build.mjs b/scripts/build.mjs index f0b6957..02cb822 100644 --- a/scripts/build.mjs +++ b/scripts/build.mjs @@ -105,7 +105,7 @@ syncInstallableSkill(sourceSkill); syncInstallableSkill(skillDist); fs.mkdirSync(path.join(dist, "release"), { recursive: true }); -zipDir(path.join(dist, "extension"), path.join(dist, "release", "opzero-chrome-extension.zip")); +zipDir(path.join(dist, "extension"), path.join(dist, "release", "browser-control-extension.zip")); zipDir(skillDist, path.join(dist, "release", "chrome-control-skill.zip")); process.stdout.write("Built dist/extension and dist/skill/chrome-control\n"); diff --git a/skills/chrome-control/scripts/check-extension-installed.js b/skills/chrome-control/scripts/check-extension-installed.js index 62a0680..1937ba0 100644 --- a/skills/chrome-control/scripts/check-extension-installed.js +++ b/skills/chrome-control/scripts/check-extension-installed.js @@ -105,7 +105,7 @@ require_effect_services.runScript(require_Layer.gen(function* () { status: "not-installed", extensionId, preferencesPath: prefPath, - message: `Opzero Chrome extension is not registered in ${prefPath}` + message: `Browser Control extension is not registered in ${prefPath}` }, 2); return; } @@ -121,7 +121,7 @@ require_effect_services.runScript(require_Layer.gen(function* () { settingsPath, state, disabledReasons, - message: `Opzero Chrome extension is installed but not enabled. state=${state}, disable_reasons=${disabledReasons}` + message: `Browser Control extension is installed but not enabled. state=${state}, disable_reasons=${disabledReasons}` }, 1); return; } @@ -132,7 +132,7 @@ require_effect_services.runScript(require_Layer.gen(function* () { preferencesPath: prefPath, settingsPath, version: settings.manifest?.version || settings.version, - message: `Opzero Chrome extension installed and enabled: ${extensionId}` + message: `Browser Control extension installed and enabled: ${extensionId}` }, 0); })); //#endregion diff --git a/skills/chrome-control/scripts/install-native-host.js b/skills/chrome-control/scripts/install-native-host.js index b9c8824..e52aebc 100644 --- a/skills/chrome-control/scripts/install-native-host.js +++ b/skills/chrome-control/scripts/install-native-host.js @@ -74,7 +74,7 @@ require_effect_services.runScript(require_Layer.gen(function* () { yield* io.writeText(hostPath, node_process.default.platform === "win32" ? windowsNativeHostLauncher() : nativeHostLauncher()); const manifest = { name: hostName, - description: "Opzero Chrome native messaging host", + description: "Browser Control native messaging host", type: "stdio", path: hostPath, allowed_origins: [`chrome-extension://${extensionId}/`] diff --git a/src/extension/background.ts b/src/extension/background.ts index 717bde2..1457cc8 100644 --- a/src/extension/background.ts +++ b/src/extension/background.ts @@ -17,8 +17,8 @@ const EXTENSION_INSTANCE_ID_KEY = "extensionInstanceId"; const PENDING_UPDATE_KEY = "opChromePendingUpdateVersion"; const RECONNECT_ALARM = `native-transport-reconnect:${HOST_NAME}`; const HEARTBEAT_ALARM = "client-heartbeat-alarm"; -const DEFAULT_SESSION_TITLE = "Chrome Control"; -const DELIVERABLE_TITLE = "✅ Chrome Control"; +const DEFAULT_SESSION_TITLE = "Browser Control"; +const DELIVERABLE_TITLE = "✅ Browser Control"; const DELIVERABLE_COLOR = "blue"; const SESSION_COLORS = ["grey", "red", "yellow", "green", "pink", "purple", "cyan", "orange"] as const; const DEBUGGER_VERSION = "1.3"; diff --git a/src/extension/manifest.json b/src/extension/manifest.json index a022aec..aa5053f 100644 --- a/src/extension/manifest.json +++ b/src/extension/manifest.json @@ -1,9 +1,9 @@ { "manifest_version": 3, "minimum_chrome_version": "106", - "name": "Chrome Control", - "description": "Control Chrome with the Chrome Control local agent bridge.", - "version": "0.2.0", + "name": "Browser Control", + "description": "Connects browser tabs to a local AI agent on your computer through a native messaging host that you install.", + "version": "0.2.1", "background": { "service_worker": "background.js" }, @@ -14,7 +14,7 @@ "128": "images/icon-128.png" }, "action": { - "default_title": "Chrome Control", + "default_title": "Browser Control", "default_popup": "popup.html", "default_icon": { "16": "images/icon-16.png", diff --git a/src/extension/popup.html b/src/extension/popup.html index 2db6d60..19dfed0 100644 --- a/src/extension/popup.html +++ b/src/extension/popup.html @@ -3,14 +3,14 @@ - Chrome Control + Browser Control
-

Chrome Control

+

Browser Control

Checking connection...

Checking @@ -39,7 +39,7 @@

Chrome Control

diff --git a/src/extension/popup.ts b/src/extension/popup.ts index cb398e9..be41d1c 100644 --- a/src/extension/popup.ts +++ b/src/extension/popup.ts @@ -27,7 +27,7 @@ function renderStatus(status: Partial = {}) { statusPill.className = `pill ${connected ? "connected" : paused ? "paused" : "disconnected"}`; statusPill.textContent = connected ? "Connected" : paused ? "Paused" : "Disconnected"; summary.textContent = connected - ? "Control Chrome with Chrome Control." + ? "Connected to the local agent host." : paused ? "Native host paused. Resume to reconnect." : "Install the native host to connect."; diff --git a/src/scripts/check-extension-installed.ts b/src/scripts/check-extension-installed.ts index d279d95..3de7152 100755 --- a/src/scripts/check-extension-installed.ts +++ b/src/scripts/check-extension-installed.ts @@ -136,7 +136,7 @@ runScript(Effect.gen(function* () { status: "not-installed", extensionId, preferencesPath: prefPath, - message: `Opzero Chrome extension is not registered in ${prefPath}` + message: `Browser Control extension is not registered in ${prefPath}` }, 2); return; } @@ -153,7 +153,7 @@ runScript(Effect.gen(function* () { settingsPath, state, disabledReasons, - message: `Opzero Chrome extension is installed but not enabled. state=${state}, disable_reasons=${disabledReasons}` + message: `Browser Control extension is installed but not enabled. state=${state}, disable_reasons=${disabledReasons}` }, 1); return; } @@ -165,6 +165,6 @@ runScript(Effect.gen(function* () { preferencesPath: prefPath, settingsPath, version: settings.manifest?.version || settings.version, - message: `Opzero Chrome extension installed and enabled: ${extensionId}` + message: `Browser Control extension installed and enabled: ${extensionId}` }, 0); })); diff --git a/src/scripts/install-native-host.ts b/src/scripts/install-native-host.ts index 3d670bc..4222411 100755 --- a/src/scripts/install-native-host.ts +++ b/src/scripts/install-native-host.ts @@ -78,7 +78,7 @@ runScript(Effect.gen(function* () { yield* io.writeText(hostPath, process.platform === "win32" ? windowsNativeHostLauncher() : nativeHostLauncher()); const manifest = { name: hostName, - description: "Opzero Chrome native messaging host", + description: "Browser Control native messaging host", type: "stdio", path: hostPath, allowed_origins: [`chrome-extension://${extensionId}/`] diff --git a/tests/acceptance/distribution.test.ts b/tests/acceptance/distribution.test.ts index e2239c8..23a4136 100644 --- a/tests/acceptance/distribution.test.ts +++ b/tests/acceptance/distribution.test.ts @@ -40,11 +40,11 @@ function runNode(args: string[], env: NodeJS.ProcessEnv = {}) { }); } -describe("Chrome Control distribution", () => { +describe("Browser Control distribution", () => { it("builds a loadable MV3 extension with self-contained browser entrypoints", () => { const manifest = readJson("dist/extension/manifest.json"); expect(manifest.manifest_version).toBe(3); - expect(manifest.name).toBe("Chrome Control"); + expect(manifest.name).toBe("Browser Control"); expect(manifest.background.service_worker).toBe("background.js"); expect(manifest.permissions).toEqual(expect.arrayContaining(["nativeMessaging", "debugger", "scripting", "tabs"])); expect(manifest.permissions).not.toEqual(expect.arrayContaining(["bookmarks", "downloads.ui", "favicon", "notifications", "readingList", "sessions", "topSites"])); @@ -81,7 +81,7 @@ describe("Chrome Control distribution", () => { "dist/skill/chrome-control/chunks", "dist/skill/chrome-control/scripts/install-native-host.js", "dist/skill/chrome-control/scripts/check-native-host-manifest.js", - "dist/release/opzero-chrome-extension.zip", + "dist/release/browser-control-extension.zip", "dist/release/chrome-control-skill.zip" ]; for (const file of files) { @@ -175,7 +175,7 @@ describe("Chrome Control distribution", () => { const manifestPath = path.join(tempDir, "com.opzero.chrome.json"); fs.writeFileSync(manifestPath, `${JSON.stringify({ name: "com.opzero.chrome", - description: "Opzero Chrome native messaging host", + description: "Browser Control native messaging host", type: "stdio", path: process.execPath, allowed_origins: [] From 4a7c4c91867ea6fd445169eb5650b9d63d5daac7 Mon Sep 17 00:00:00 2001 From: afif Date: Mon, 28 Sep 2026 14:17:41 +0800 Subject: [PATCH 06/17] Cover native host reload, pause and resume in the service worker Pause must survive reconnect alarms and a service worker restart until the user resumes. Reload must drop active sessions, open a fresh native port, and the reconnect alarm must recover after the host exits. --- tests/security/native-host-controls.test.ts | 48 +++++++++++++++++++++ tests/support/background.ts | 11 +++-- 2 files changed, 55 insertions(+), 4 deletions(-) create mode 100644 tests/security/native-host-controls.test.ts diff --git a/tests/security/native-host-controls.test.ts b/tests/security/native-host-controls.test.ts new file mode 100644 index 0000000..c528a5b --- /dev/null +++ b/tests/security/native-host-controls.test.ts @@ -0,0 +1,48 @@ +import { expect, it, vi } from "vitest"; +import { background } from "../support/background"; + +const reconnectAlarm = { name: "native-transport-reconnect:com.opzero.chrome" }; + +it("keeps the native host paused across reconnect alarms and worker restarts until resumed", async () => { + const b = await background(); + expect(b.chrome.runtime.connectNative).toHaveBeenCalledTimes(1); + + const paused = await b.popup("PAUSE_NATIVE_HOST"); + expect(paused.status.state).toBe("paused"); + expect(b.port.disconnect).toHaveBeenCalledTimes(1); + expect(b.storage.NATIVE_HOST_PAUSED).toBe(true); + expect(b.chrome.alarms.clear).toHaveBeenCalledWith(reconnectAlarm.name); + + b.chrome.alarms.onAlarm.emit(reconnectAlarm); + expect((await b.popup("GET_NATIVE_HOST_STATUS")).status.state).toBe("paused"); + expect(b.chrome.runtime.connectNative).toHaveBeenCalledTimes(1); + + const restarted = await background({ NATIVE_HOST_PAUSED: true }); + expect(restarted.chrome.runtime.connectNative).not.toHaveBeenCalled(); + expect((await restarted.popup("GET_NATIVE_HOST_STATUS")).status.state).toBe("paused"); + + const resumed = await restarted.popup("RESUME_NATIVE_HOST"); + expect(resumed.status.state).toBe("connected"); + expect(restarted.chrome.runtime.connectNative).toHaveBeenCalledTimes(1); + expect(restarted.storage.NATIVE_HOST_PAUSED).toBe(false); +}); + +it("reloads the native host on a fresh port and reconnects after the host exits", async () => { + const b = await background(); + const owner = { session_id: "owner", turn_id: "turn", tabId: 1, expectedOrigin: "https://synthetic.invalid" }; + await b.rpc("createTab", owner); + expect((await b.rpc("bindPage", owner)).error).toBeUndefined(); + + const reloaded = await b.popup("RELOAD_NATIVE_HOST"); + expect(reloaded.status.state).toBe("connected"); + expect(b.port.disconnect).toHaveBeenCalledTimes(1); + expect(b.chrome.runtime.connectNative).toHaveBeenCalledTimes(2); + expect(b.storage.NATIVE_HOST_PAUSED).toBe(false); + expect((await b.rpc("bindPage", owner)).error).toBeDefined(); + + b.port.onDisconnect.emit(); + await vi.waitFor(() => expect((b.storage.NATIVE_HOST_STATUS as { state: string }).state).toBe("disconnected")); + b.chrome.alarms.onAlarm.emit(reconnectAlarm); + expect(b.chrome.runtime.connectNative).toHaveBeenCalledTimes(3); + await vi.waitFor(() => expect((b.storage.NATIVE_HOST_STATUS as { state: string }).state).toBe("connected")); +}); diff --git a/tests/support/background.ts b/tests/support/background.ts index 27305de..c3ceda5 100644 --- a/tests/support/background.ts +++ b/tests/support/background.ts @@ -8,15 +8,15 @@ function event() { return { addListener: (fn: (...args: any[]) => void) => listeners.push(fn), emit: (...args: any[]) => listeners.forEach(fn => fn(...args)) }; } -export async function background() { +export async function background(initialStorage: Record = {}) { const responses: any[] = []; const tabs = new Map(); let nextTab = 1; const port = { onMessage: event(), onDisconnect: event(), postMessage: (msg: any) => responses.push(msg), disconnect: vi.fn() }; - const storage: Record = {}; + const storage: Record = { ...initialStorage }; let group = { id: 1, title: "", collapsed: false }; const chrome = { - runtime: { connectNative: () => port, getManifest: () => ({ version: "test" }), id: "test", onMessage: event(), onStartup: event(), onInstalled: event(), onUpdateAvailable: event(), reload: vi.fn() }, + runtime: { connectNative: vi.fn(() => port), getManifest: () => ({ version: "test" }), id: "test", onMessage: event(), onStartup: event(), onInstalled: event(), onUpdateAvailable: event(), reload: vi.fn() }, storage: { local: { get: (key: string, cb: Function) => cb({ [key]: storage[key] }), set: (value: object, cb: Function) => { Object.assign(storage, value); cb(); } }, session: { get: (_: unknown, cb: Function) => cb({}) } }, alarms: { create: vi.fn(), clear: vi.fn(), onAlarm: event() }, windows: { getCurrent: (_: unknown, cb: Function) => cb({ id: 1, type: "normal" }) }, @@ -45,5 +45,8 @@ export async function background() { await vi.waitFor(() => expect(responses.find(r => r.id === requestId)).toBeDefined()); return responses.find(r => r.id === requestId); } - return { chrome, port, responses, rpc }; + function popup(type: string) { + return new Promise(resolve => chrome.runtime.onMessage.emit({ type }, {}, resolve)); + } + return { chrome, port, responses, rpc, popup, storage }; } From a88821b3295861011961c028ecf80c199f67e844 Mon Sep 17 00:00:00 2001 From: afif Date: Mon, 28 Sep 2026 14:49:10 +0800 Subject: [PATCH 07/17] Drop the unused history and downloads permissions No client calls getUserHistory or consumes onDownloadChange, including the fast-chrome controller. The Web Store rejects versions that request permissions the item does not need. The project check now pins the exact permission list and refuses a manifest key in the store package. --- scripts/check-project.js | 6 ++-- skills/chrome-control/native-host/host.js | 2 +- src/extension/background.ts | 40 ----------------------- src/extension/manifest.json | 2 -- src/native-host/host.ts | 2 +- tests/acceptance/distribution.test.ts | 3 +- tests/security/background.test.ts | 2 -- tests/support/background.ts | 2 -- 8 files changed, 7 insertions(+), 52 deletions(-) diff --git a/scripts/check-project.js b/scripts/check-project.js index 1af8df2..e27aa20 100755 --- a/scripts/check-project.js +++ b/scripts/check-project.js @@ -58,9 +58,9 @@ for (const file of requiredFiles) { } const manifest = JSON.parse(fs.readFileSync(path.join(root, "dist/extension/manifest.json"), "utf8")); -for (const permission of ["debugger", "nativeMessaging", "scripting", "downloads", "history", "tabGroups", "tabs"]) { - if (!manifest.permissions.includes(permission)) failures.push(`Manifest missing ${permission}`); -} +const expectedPermissions = ["alarms", "debugger", "nativeMessaging", "scripting", "storage", "tabGroups", "tabs"]; +if (JSON.stringify(manifest.permissions) !== JSON.stringify(expectedPermissions)) failures.push(`Manifest permissions must be exactly ${expectedPermissions.join(", ")}`); +if ("key" in manifest) failures.push("Store package manifest must not contain a key field"); if (manifest.manifest_version !== 3) failures.push("Manifest is not MV3"); if (manifest.background?.service_worker !== "background.js") failures.push("Manifest background service worker mismatch"); for (const [size, file] of Object.entries({ 16: "images/icon-16.png", 32: "images/icon-32.png", 48: "images/icon-48.png", 128: "images/icon-128.png" })) { diff --git a/skills/chrome-control/native-host/host.js b/skills/chrome-control/native-host/host.js index 4fa34a2..6da2997 100644 --- a/skills/chrome-control/native-host/host.js +++ b/skills/chrome-control/native-host/host.js @@ -98,7 +98,7 @@ function handleNative(message) { if (message.method === "ping") native(result(message.id, "pong")); else if (message.method === "getHostInfo") native(result(message.id, { name: "opzero-chrome-native-host", - version: "0.2.0", + version: "0.2.1", protocolVersion: 2, extensionProtocol, epoch, diff --git a/src/extension/background.ts b/src/extension/background.ts index 1457cc8..653f428 100644 --- a/src/extension/background.ts +++ b/src/extension/background.ts @@ -799,21 +799,6 @@ const api: Record = { }); }), - getUserHistory: (params: JsonRecord = {}) => Effect.gen(function* () { - const chromeApi = yield* ChromeApi; - yield* Effect.try({ try: () => ensureSession(params), catch: toError }); - const maxResults = Math.max(1, Math.min(Number(params.limit) || 100, 1000)); - const from = Number(params.from); - const to = Number(params.to); - const query: chrome.history.HistoryQuery = { - text: typeof params.query === "string" ? params.query : "", - maxResults, - startTime: Number.isFinite(from) ? from : 0 - }; - if (Number.isFinite(to)) query.endTime = to; - return yield* chromeApi.call("history", "search", query); - }), - createTab: (params: JsonRecord = {}) => Effect.gen(function* () { const chromeApi = yield* ChromeApi; const session = yield* Effect.try({ try: () => ensureSession(params), catch: toError }); @@ -1337,31 +1322,6 @@ chrome.debugger.onDetach.addListener((source, reason) => { if (session) nativeTransport?.notify("onCDPDetach", { session_id: session.id, source, reason }); }); -chrome.downloads.onCreated.addListener((item) => { - if (!sessions.size) return; - nativeTransport?.notify("onDownloadChange", { - id: String(item.id), - filename: item.filename, - url: item.url, - status: "started" - }); -}); - -chrome.downloads.onChanged.addListener((delta) => { - if (!sessions.size) return; - let status = delta.state?.current; - if (status === "complete") status = "complete"; - else if (status === "interrupted") status = delta.error?.current === "USER_CANCELED" ? "canceled" : "failed"; - else if (status) status = "in_progress"; - if (!status) return; - nativeTransport?.notify("onDownloadChange", { - id: String(delta.id), - filename: delta.filename?.current, - url: delta.url?.current, - status - }); -}); - chrome.tabs.onRemoved.addListener((tabId) => { invalidateTab(tabId); const session = getSessionForTab(tabId); diff --git a/src/extension/manifest.json b/src/extension/manifest.json index aa5053f..79ddf74 100644 --- a/src/extension/manifest.json +++ b/src/extension/manifest.json @@ -26,8 +26,6 @@ "permissions": [ "alarms", "debugger", - "downloads", - "history", "nativeMessaging", "scripting", "storage", diff --git a/src/native-host/host.ts b/src/native-host/host.ts index 59303bc..f4df43a 100755 --- a/src/native-host/host.ts +++ b/src/native-host/host.ts @@ -75,7 +75,7 @@ function handleNative(message: JsonRpcMessage) { if (message.id != null) { if (message.method === "ping") native(result(message.id, "pong")); else if (message.method === "getHostInfo") native(result(message.id, { - name: "opzero-chrome-native-host", version: "0.2.0", protocolVersion: 2, extensionProtocol, epoch, pid: process.pid, + name: "opzero-chrome-native-host", version: "0.2.1", protocolVersion: 2, extensionProtocol, epoch, pid: process.pid, transport: useTcp ? "tcp" : "unix", endpoint: useTcp ? `127.0.0.1:${port}` : socketPath })); else native(error(message.id, "Unsupported native host method")); diff --git a/tests/acceptance/distribution.test.ts b/tests/acceptance/distribution.test.ts index 23a4136..73311d4 100644 --- a/tests/acceptance/distribution.test.ts +++ b/tests/acceptance/distribution.test.ts @@ -47,7 +47,8 @@ describe("Browser Control distribution", () => { expect(manifest.name).toBe("Browser Control"); expect(manifest.background.service_worker).toBe("background.js"); expect(manifest.permissions).toEqual(expect.arrayContaining(["nativeMessaging", "debugger", "scripting", "tabs"])); - expect(manifest.permissions).not.toEqual(expect.arrayContaining(["bookmarks", "downloads.ui", "favicon", "notifications", "readingList", "sessions", "topSites"])); + expect(manifest.permissions).toEqual(["alarms", "debugger", "nativeMessaging", "scripting", "storage", "tabGroups", "tabs"]); + expect(manifest.host_permissions).toEqual([""]); for (const [size, file] of Object.entries({ "16": "images/icon-16.png", diff --git a/tests/security/background.test.ts b/tests/security/background.test.ts index 49c340e..a09d2aa 100644 --- a/tests/security/background.test.ts +++ b/tests/security/background.test.ts @@ -166,8 +166,6 @@ it("advertises protocol v2 and gates profile reads after revocation", async () = await b.rpc("createTab", params); b.port.onMessage.emit({ jsonrpc: "2.0", method: "internal.releaseClient", params }); expect((await b.rpc("getUserTabs", params)).error).toBeDefined(); - expect((await b.rpc("getUserHistory", params)).error).toBeDefined(); - expect(b.chrome.history.search).not.toHaveBeenCalled(); expect((await b.rpc("constructor", params)).error.message).toContain("Unsupported browser command"); }); diff --git a/tests/support/background.ts b/tests/support/background.ts index c3ceda5..a1bac05 100644 --- a/tests/support/background.ts +++ b/tests/support/background.ts @@ -27,13 +27,11 @@ export async function background(initialStorage: Record = {}) { query: (_: unknown, cb: Function) => cb([...tabs.values()]), onRemoved: event(), onReplaced: event(), onUpdated: event() }, - history: { search: vi.fn((_: unknown, cb: Function) => cb([])) }, tabGroups: { get: vi.fn((_: unknown, cb: Function) => cb({ ...group })), update: vi.fn((_: unknown, value: object, cb: Function) => { group = { ...group, ...value }; cb({ ...group }); }) }, debugger: { attach: vi.fn((_: unknown, __: unknown, cb: Function) => cb()), detach: (_: unknown, cb: Function) => cb(), getTargets: (cb: Function) => cb([{ tabId: 1 }, { tabId: 2 }]), sendCommand: vi.fn((_: unknown, __: unknown, ___: unknown, cb: Function) => cb({})), onEvent: event(), onDetach: event() }, - downloads: { onCreated: event(), onChanged: event() }, scripting: { executeScript: vi.fn((_: unknown, cb: Function) => cb([{ documentId: "doc-1", frameId: 0, result: { status: "observed", origin: "https://synthetic.invalid", url: "https://synthetic.invalid/" } }])) } }; vm.runInNewContext(fs.readFileSync("dist/extension/background.js", "utf8"), { chrome, crypto: webcrypto, setTimeout, clearTimeout, console, URL, TextEncoder, TextDecoder, AbortController }); From 1706c5fcaf685ae705c4fe54e19cba958a79d6cc Mon Sep 17 00:00:00 2001 From: afif Date: Mon, 28 Sep 2026 15:12:12 +0800 Subject: [PATCH 08/17] Publish to the Chrome Web Store through keyless Workload Identity The OAuth refresh token expired because its consent screen was in testing mode. GitHub Actions now signs in as the cws-publisher service account through Workload Identity Federation, so the repository holds no Google credential. The workflow can read the item status, upload a draft package, or upload and submit for review. Also update the README and developer guide for the Browser Control name, the store and unpacked extension IDs, and the host controls. --- .github/workflows/chrome-web-store.yml | 72 +++++++++++++++---------- README.md | 72 ++++++++++++------------- docs/DEVELOPER.md | 34 +++++++++--- scripts/install-chrome-control-skill.sh | 2 +- 4 files changed, 107 insertions(+), 73 deletions(-) diff --git a/.github/workflows/chrome-web-store.yml b/.github/workflows/chrome-web-store.yml index 9d78338..7ff4ab3 100644 --- a/.github/workflows/chrome-web-store.yml +++ b/.github/workflows/chrome-web-store.yml @@ -3,67 +3,85 @@ name: Chrome Web Store on: workflow_dispatch: inputs: - tag: - description: "Git tag to publish, for example vX.Y.Z" + ref: + description: "Git tag or branch to build, for example vX.Y.Z" required: true type: string + action: + description: "status reads the item state; upload replaces the draft package; submit uploads and submits for review" + required: true + default: status + type: choice + options: + - status + - upload + - submit -run-name: Publish ${{ inputs.tag }} to Chrome Web Store +run-name: Chrome Web Store ${{ inputs.action }} ${{ inputs.ref }} permissions: contents: read + id-token: write jobs: publish: runs-on: ubuntu-latest env: EXTENSION_ID: ${{ secrets.CHROME_EXTENSION_ID }} + CHROME_PUBLISHER_ID: ${{ secrets.CHROME_PUBLISHER_ID }} steps: - uses: actions/checkout@v4 with: - ref: ${{ inputs.tag }} + ref: ${{ inputs.ref }} - uses: pnpm/action-setup@v4 + if: ${{ inputs.action != 'status' }} with: version: 10.33.2 - uses: actions/setup-node@v4 + if: ${{ inputs.action != 'status' }} with: node-version: 22 cache: pnpm - - run: pnpm install --frozen-lockfile - - run: pnpm run check + - if: ${{ inputs.action != 'status' }} + run: pnpm install --frozen-lockfile + + - if: ${{ inputs.action != 'status' }} + run: pnpm run check - name: Get Chrome Web Store access token - id: token + id: auth + uses: google-github-actions/auth@v2 + with: + workload_identity_provider: ${{ vars.GCP_WORKLOAD_IDENTITY_PROVIDER }} + service_account: ${{ vars.GCP_SERVICE_ACCOUNT }} + token_format: access_token + access_token_scopes: https://www.googleapis.com/auth/chromewebstore + create_credentials_file: false + + - name: Fetch item status shell: bash env: - CHROME_CLIENT_ID: ${{ secrets.CHROME_CLIENT_ID }} - CHROME_CLIENT_SECRET: ${{ secrets.CHROME_CLIENT_SECRET }} - CHROME_REFRESH_TOKEN: ${{ secrets.CHROME_REFRESH_TOKEN }} + ACCESS_TOKEN: ${{ steps.auth.outputs.access_token }} run: | set -euo pipefail - response="$(curl -sS -w '\n%{http_code}' https://oauth2.googleapis.com/token \ - -d "client_id=${CHROME_CLIENT_ID}" \ - -d "client_secret=${CHROME_CLIENT_SECRET}" \ - -d "refresh_token=${CHROME_REFRESH_TOKEN}" \ - -d "grant_type=refresh_token")" + response="$(curl -sS -w '\n%{http_code}' \ + -H "Authorization: Bearer ${ACCESS_TOKEN}" \ + "https://chromewebstore.googleapis.com/v2/publishers/${CHROME_PUBLISHER_ID}/items/${EXTENSION_ID}:fetchStatus")" status="$(printf '%s' "$response" | tail -n 1)" body="$(printf '%s' "$response" | sed '$d')" - if [ "$status" != "200" ]; then - printf 'Chrome Web Store token request failed with HTTP %s:\n%s\n' "$status" "$body" >&2 + printf 'Chrome Web Store status returned HTTP %s:\n%s\n' "$status" "$body" + if [ "$status" -lt 200 ] || [ "$status" -ge 300 ]; then exit 1 fi - token="$(node -e 'process.stdout.write(JSON.parse(process.argv[1]).access_token)' "$body")" - echo "::add-mask::$token" - echo "access_token=$token" >> "$GITHUB_OUTPUT" - name: Upload extension package + if: ${{ inputs.action != 'status' }} shell: bash env: - ACCESS_TOKEN: ${{ steps.token.outputs.access_token }} - CHROME_PUBLISHER_ID: ${{ secrets.CHROME_PUBLISHER_ID }} + ACCESS_TOKEN: ${{ steps.auth.outputs.access_token }} run: | set -euo pipefail response="$(curl -sS -w '\n%{http_code}' -X POST \ @@ -72,16 +90,16 @@ jobs: "https://chromewebstore.googleapis.com/upload/v2/publishers/${CHROME_PUBLISHER_ID}/items/${EXTENSION_ID}:upload")" status="$(printf '%s' "$response" | tail -n 1)" body="$(printf '%s' "$response" | sed '$d')" + printf 'Chrome Web Store upload returned HTTP %s:\n%s\n' "$status" "$body" if [ "$status" -lt 200 ] || [ "$status" -ge 300 ]; then - printf 'Chrome Web Store upload failed with HTTP %s:\n%s\n' "$status" "$body" >&2 exit 1 fi - - name: Publish extension + - name: Submit for review + if: ${{ inputs.action == 'submit' }} shell: bash env: - ACCESS_TOKEN: ${{ steps.token.outputs.access_token }} - CHROME_PUBLISHER_ID: ${{ secrets.CHROME_PUBLISHER_ID }} + ACCESS_TOKEN: ${{ steps.auth.outputs.access_token }} run: | set -euo pipefail response="$(curl -sS -w '\n%{http_code}' -X POST \ @@ -89,7 +107,7 @@ jobs: "https://chromewebstore.googleapis.com/v2/publishers/${CHROME_PUBLISHER_ID}/items/${EXTENSION_ID}:publish")" status="$(printf '%s' "$response" | tail -n 1)" body="$(printf '%s' "$response" | sed '$d')" + printf 'Chrome Web Store publish returned HTTP %s:\n%s\n' "$status" "$body" if [ "$status" -lt 200 ] || [ "$status" -ge 300 ]; then - printf 'Chrome Web Store publish failed with HTTP %s:\n%s\n' "$status" "$body" >&2 exit 1 fi diff --git a/README.md b/README.md index f58f1b8..cf1f51a 100644 --- a/README.md +++ b/README.md @@ -1,69 +1,65 @@ -# Opzero Chrome +# Browser Control -Chrome automation extension for Opzero agents. +Browser Control is a Chrome extension and native messaging host that let an AI agent on your own computer work in your browser tabs. The extension does nothing until you install the local host. -## Install For Users +- Homepage: +- Privacy policy: +- Support: -Install Opzero Chrome from the Chrome Web Store: +## Identifiers -```text -https://chromewebstore.google.com/detail/opzero-chrome/dcnjjnecbhipdbngkhjppkckpkellmld -``` +| What | Value | +| --- | --- | +| Chrome Web Store item ID | `dcnjjnecbhipdbngkhjppkckpkellmld` | +| Native messaging host name | `com.opzero.chrome` | +| Host and page protocol | version 2 | +| Unpacked extension ID | Derived by Chrome from the absolute path of the loaded folder. The manifest has no `key` field. | -For local development or manual testing, you can still download `opzero-chrome-extension.zip` from the latest GitHub Release, unzip it, then load the folder from `chrome://extensions` with Developer mode enabled. +Chrome derives an unpacked extension's ID from its folder path, so every checkout or install location has its own ID. For example, the fast-chrome controller loads `~/.config/opencode/mcp/fast-chrome/op-chrome/dist/extension`, which gives the ID `pncpgnbanebkeopjghjleodgmphmmmcp`. The native host manifest must allow the ID of the extension that connects to it. -## Install The Agent Skill +## Install for users -Install the `chrome-control` skill from the repository path with any agent runtime that supports repository/path-based skill installs: +1. Install Browser Control from the [Chrome Web Store](https://chromewebstore.google.com/detail/dcnjjnecbhipdbngkhjppkckpkellmld). +2. Install the native host. It needs Node.js 18 or later on macOS or Linux: -```text -https://github.com/opzero1/op-chrome/tree/main/skills/chrome-control -``` + ```sh + mkdir -p ~/.config/opencode/skills/chrome-control + curl -fsSL https://github.com/opzero1/browser-control/releases/latest/download/chrome-control-skill.zip -o /tmp/chrome-control-skill.zip + unzip -o /tmp/chrome-control-skill.zip -d ~/.config/opencode/skills/chrome-control + cd ~/.config/opencode/skills/chrome-control + node scripts/install-native-host.js --extension-id dcnjjnecbhipdbngkhjppkckpkellmld + ``` -Alternatively, install the latest release zip into opencode's global skills directory with one command: +3. Open the Browser Control popup and click **Reload host**. It shows **Connected**. -```sh -curl -fsSL https://raw.githubusercontent.com/opzero1/op-chrome/main/scripts/install-chrome-control-skill.sh | sh -``` +The popup also has **Pause host**, which disconnects the host and ends every agent session until you click **Resume host**. The pause setting survives browser restarts. If the host exits, the extension reconnects automatically within about 30 seconds. -Manual install: +To install the agent skill with one command instead: ```sh -mkdir -p ~/.config/opencode/skills/chrome-control -curl -fsSL https://github.com/opzero1/op-chrome/releases/latest/download/chrome-control-skill.zip -o /tmp/chrome-control-skill.zip -unzip -o /tmp/chrome-control-skill.zip -d ~/.config/opencode/skills/chrome-control -``` - -Trigger it in agents with `@chrome-control`, or by asking for Chrome/browser automation, Chrome setup checks, native host repair, tab/session control, or CDP access. - -After installing the skill, run the native host installer from the installed skill directory: - -```sh -node scripts/install-native-host.js --extension-id "$(node -p 'require("./scripts/extension-id.json").extensionId')" +curl -fsSL https://raw.githubusercontent.com/opzero1/browser-control/main/scripts/install-chrome-control-skill.sh | sh ``` ## Verify ```sh -pnpm run check -pnpm run check-native-host -- --extension-id --json -pnpm run check-extension -- --extension-id --json -pnpm run client -- ping -pnpm run client -- getInfo +node native-host/client.js ping +node native-host/client.js getInfo ``` -If `ping` and `getInfo` work, Chrome, the extension, and the native host are connected. +`ping` returns `pong` from the extension. `getInfo` reports `protocolVersion: 2`. ## Development -See [docs/DEVELOPER.md](docs/DEVELOPER.md) for local setup and native host installation. +See [docs/DEVELOPER.md](docs/DEVELOPER.md) to load an unpacked build and connect a local host. ```sh pnpm install -pnpm run build pnpm run check ``` +`pnpm run check` builds the extension, native host and installable skill, then runs type checks, tests and the project checks. The headless-browser tests for private input run only when `OPZERO_SYNTHETIC_CHROME` names a Chrome for Testing binary. + ## Release -See [docs/RELEASE.md](docs/RELEASE.md). +See [docs/RELEASE.md](docs/RELEASE.md). The store listing text is in [store/listing.md](store/listing.md), the store images are in [store/assets](store/assets), and the website is in [site](site). diff --git a/docs/DEVELOPER.md b/docs/DEVELOPER.md index 5d1c173..68f018c 100644 --- a/docs/DEVELOPER.md +++ b/docs/DEVELOPER.md @@ -1,15 +1,17 @@ # Developer Setup -## Local Extension Install +## Local extension install -1. Download `opzero-chrome-extension.zip` from the latest GitHub Release, or run `pnpm run build` and use `dist/extension`. +1. Run `pnpm install && pnpm run build`, or download `browser-control-extension.zip` from the latest GitHub Release and unzip it. 2. Open `chrome://extensions`. 3. Enable **Developer mode**. 4. Click **Load unpacked**. -5. Select the unzipped extension folder or `dist/extension`. -6. Copy the generated extension ID. +5. Select `dist/extension` or the unzipped folder. +6. Copy the extension ID that Chrome shows. -## Native Host +The manifest has no `key` field, so Chrome derives the unpacked ID from the folder's absolute path. Loading the same folder always gives the same ID; moving it gives a new one. + +## Native host From a local checkout: @@ -18,9 +20,21 @@ pnpm install pnpm run install-native-host -- --extension-id ``` -Reload the extension in `chrome://extensions` after installing the native host. +Open the extension popup and click **Reload host** after you install the native host. + +The installer writes `com.opzero.chrome.json` into Chrome's per-user `NativeMessagingHosts` directory, and it saves the extension ID to `dist/scripts/extension-id.json` so follow-up checks can run without `--extension-id`. Pass `--socket-path ` to give the host a private socket other than `~/.opzero-chrome/default.sock`. + +For a disposable Chrome for Testing profile, write the host manifest to `/NativeMessagingHosts/` instead, so the default Chrome profile keeps its own host. `store/capture/launch.sh` shows the full sequence. + +## Host controls + +The popup has three controls: -The installer saves the extension ID to `dist/scripts/extension-id.json` so follow-up checks can run without passing `--extension-id` again. +- **Reload host** closes the native port and opens a new one. Chrome restarts the host process, and every connected agent session ends. +- **Pause host** disconnects the host, stops the reconnect and heartbeat alarms, and ends every session. The paused state is stored and survives service worker and browser restarts. +- **Resume host** clears the paused state and reconnects. + +When the host exits, the extension shows **Disconnected** and its reconnect alarm reconnects within 30 seconds. ## Verify @@ -31,3 +45,9 @@ pnpm run check-extension -- --extension-id --json pnpm run client -- ping pnpm run client -- getInfo ``` + +To also run the headless-browser private-input tests, point `OPZERO_SYNTHETIC_CHROME` at a Chrome for Testing binary: + +```sh +OPZERO_SYNTHETIC_CHROME="/path/to/Google Chrome for Testing" pnpm exec vitest run tests/security/private-input.browser.test.ts +``` diff --git a/scripts/install-chrome-control-skill.sh b/scripts/install-chrome-control-skill.sh index a2f8d24..4d11348 100644 --- a/scripts/install-chrome-control-skill.sh +++ b/scripts/install-chrome-control-skill.sh @@ -1,7 +1,7 @@ #!/usr/bin/env sh set -eu -RELEASE_URL="${CHROME_CONTROL_SKILL_URL:-https://github.com/opzero1/op-chrome/releases/latest/download/chrome-control-skill.zip}" +RELEASE_URL="${CHROME_CONTROL_SKILL_URL:-https://github.com/opzero1/browser-control/releases/latest/download/chrome-control-skill.zip}" INSTALL_DIR="${CHROME_CONTROL_SKILL_DIR:-$HOME/.config/opencode/skills/chrome-control}" TMP_DIR="$(mktemp -d)" ZIP_PATH="$TMP_DIR/chrome-control-skill.zip" From 709728963909ddca323b971bf3d455239d046b38 Mon Sep 17 00:00:00 2001 From: afif Date: Mon, 28 Sep 2026 15:17:01 +0800 Subject: [PATCH 09/17] Add the Browser Control website, store listing and real-use images The site is served from Cloudflare Pages at browser-control.pages.dev and holds the homepage, privacy policy, support page and reviewer steps. Google rejected 0.1.3 because its privacy link pointed to a GitHub file; the listing now points to the hosted policy. store/listing.md holds every dashboard field, with permission justifications and data disclosures traced to the source. The store images are captures of the real extension in a disposable Chrome for Testing profile, and store/capture can recreate them. --- docs/PRIVACY.md | 125 +++++- docs/RELEASE.md | 94 ++++- site/404.html | 51 +++ site/_headers | 10 + site/icon.png | Bin 0 -> 3751 bytes site/index.html | 117 ++++++ site/privacy/index.html | 200 +++++++++ site/styles.css | 385 ++++++++++++++++++ site/support/index.html | 134 ++++++ site/support/reviewers/index.html | 171 ++++++++ store/assets/README.md | 53 +++ store/assets/marquee-1400x560.png | Bin 0 -> 238709 bytes store/assets/screenshot-1-popup-connected.png | Bin 0 -> 124178 bytes .../screenshot-2-agent-filling-form.png | Bin 0 -> 110878 bytes .../assets/screenshot-3-deliverable-group.png | Bin 0 -> 105693 bytes .../screenshot-4-observing-wikipedia.png | Bin 0 -> 241703 bytes store/assets/small-promo-440x280.png | Bin 0 -> 61880 bytes store/capture/ax.sh | 28 ++ store/capture/cleanup.sh | 13 + store/capture/cmd.sh | 13 + store/capture/demo/index.html | 85 ++++ store/capture/drive.mjs | 122 ++++++ store/capture/env.sh | 20 + store/capture/extension-id.py | 8 + store/capture/launch.sh | 61 +++ store/capture/promo/marquee.html | 34 ++ store/capture/promo/small.html | 27 ++ store/capture/render-promo.sh | 19 + store/capture/render.mjs | 47 +++ store/capture/serve-demo.sh | 9 + store/capture/shot.sh | 18 + store/listing.md | 308 ++++++++++++++ store/reviewer-test-instructions.md | 163 ++++++++ 33 files changed, 2271 insertions(+), 44 deletions(-) create mode 100644 site/404.html create mode 100644 site/_headers create mode 100644 site/icon.png create mode 100644 site/index.html create mode 100644 site/privacy/index.html create mode 100644 site/styles.css create mode 100644 site/support/index.html create mode 100644 site/support/reviewers/index.html create mode 100644 store/assets/README.md create mode 100644 store/assets/marquee-1400x560.png create mode 100644 store/assets/screenshot-1-popup-connected.png create mode 100644 store/assets/screenshot-2-agent-filling-form.png create mode 100644 store/assets/screenshot-3-deliverable-group.png create mode 100644 store/assets/screenshot-4-observing-wikipedia.png create mode 100644 store/assets/small-promo-440x280.png create mode 100755 store/capture/ax.sh create mode 100755 store/capture/cleanup.sh create mode 100755 store/capture/cmd.sh create mode 100644 store/capture/demo/index.html create mode 100755 store/capture/drive.mjs create mode 100755 store/capture/env.sh create mode 100755 store/capture/extension-id.py create mode 100755 store/capture/launch.sh create mode 100644 store/capture/promo/marquee.html create mode 100644 store/capture/promo/small.html create mode 100755 store/capture/render-promo.sh create mode 100644 store/capture/render.mjs create mode 100755 store/capture/serve-demo.sh create mode 100755 store/capture/shot.sh create mode 100644 store/listing.md create mode 100644 store/reviewer-test-instructions.md diff --git a/docs/PRIVACY.md b/docs/PRIVACY.md index 471284d..6c708c7 100644 --- a/docs/PRIVACY.md +++ b/docs/PRIVACY.md @@ -1,41 +1,122 @@ -# Opzero Chrome Privacy Policy +# Browser Control privacy policy -Effective date: May 13, 2026 +Effective date: September 28, 2026 -Opzero Chrome is a local browser automation extension. It connects Chrome to a native messaging host on the user's machine so an installed Opzero agent skill can control tabs, inspect pages, and run Chrome Debugger Protocol commands at the user's request. +This policy explains how the Browser Control extension for Google Chrome™ ("the extension") and its local helper program, the native messaging host ("the host"), handle data. The Browser Control maintainers ("we") publish both as open-source software at . -## Data Handled +The same policy is published at . -The extension may handle the following data during an active local agent session: +## Summary -- Open tab metadata, including tab IDs, window IDs, URLs, titles, and group state. -- Page interaction data returned by Chrome Debugger Protocol commands requested by the local agent. -- Download metadata, including download IDs, filenames, URLs, state, danger status, and error status. -- Browser history search results when the local agent explicitly requests history. -- Extension connection state, extension instance ID, native host status, session state, and pending update markers. +- Browser Control connects your browser tabs to an AI agent that you run on your own computer. +- It works only after you install the host on the same computer. Without the host, the extension is idle. +- The extension sends data only to the host. The host sends it only to the local agent software that connects to it. +- We do not collect any data. The extension and the host send nothing to us or to any server that we run. We do not sell data, show ads, use analytics or load remote code. -## How Data Is Used +## How data moves -Data is used only to provide local, user-requested browser automation and extension connection status. Opzero Chrome does not sell user data, use user data for advertising, build advertising profiles, or transfer browsing data to an Opzero-operated remote service. +1. **Extension to host.** The extension talks to the host through Chrome native messaging, over the standard input and output of a local process. Chrome starts the host, named `com.opzero.chrome`, only if you installed it. The host's manifest allows only the extension ID that you gave the installer. For the Chrome Web Store version, that ID is `dcnjjnecbhipdbngkhjppkckpkellmld`. +2. **Host to your local agent.** The host accepts connections only on a private endpoint on your computer. By default this is a Unix socket at `~/.opzero-chrome/default.sock`, in a folder that is restricted to your user account. On Windows, or if you configure it, the host listens on the loopback address `127.0.0.1` and requires a secret token that it reads from a private file. The host does not accept connections from other computers. +3. **Separate sessions.** Each connection to the host gets its own session. The answer to a request goes back only to the connection that sent it. Events from a tab go only to the connection whose session owns that tab. A notice that control stopped goes to every connection. +4. **Your local agent.** The agent is separate software that you choose and run. We do not make it, and we do not control what it does with data. For example, an AI agent can send page text or screenshots to the provider of its AI model. Check the privacy terms of your agent and of any service that it uses. +5. **Websites.** When your agent navigates, clicks, submits a form or attaches a file, Chrome sends the resulting requests to the website, the same as if you did it yourself. A file attached to an upload field goes to that website when the form is submitted. -The extension communicates with the native messaging host configured for the installed extension ID. What the local agent does with browser data is controlled by the user's local agent environment and the commands the user gives it. +## Data the extension handles -Opzero Chrome's use and transfer of information received from Chrome APIs follows the Chrome Web Store User Data Policy, including Limited Use requirements. +The extension handles data only when your local agent asks for it. "Agent tabs" are the tabs that the agent opened and the tabs that it claimed from your open tabs. The extension puts agent tabs in a labeled tab group. -## Storage +| Data | When | Why | +| --- | --- | --- | +| Tab details: tab and window IDs, position, URL, title, whether the tab is active, and its tab group | The agent lists your open tabs (Chrome's internal pages are left out), opens a tab or claims a tab. | So that the agent can choose a tab and work in it. | +| Page content from agent tabs: URL, title, visible text (up to 12,000 characters) and the labels of up to 100 buttons, links and form fields | The agent reads a page. | So that the agent can understand the page and choose an action. Page readings never include what is typed in form fields. They leave out password fields, one-time-code fields and the labels of those fields. | +| Screenshots of agent tabs (JPEG images) | The agent asks for a screenshot or a short recording. | So that the agent can see the page. | +| Clicks and typed text | The agent clicks a control or types into a field that it found on the page (up to 2,000 characters for each field). | To carry out the task. | +| Chrome DevTools Protocol results and events for agent tabs | The agent attaches the debugger to one of its tabs and sends DevTools Protocol commands. | For advanced automation. Depending on the commands and the events that the agent turns on, this can include the page structure, the results of scripts that the agent runs in the page, the addresses of pages and resources that the tab loads, other network activity and page events, and the page's cookies. | +| Sign-in values, such as a user name, password or one-time code | A local program on your computer asks the host to fill sign-in fields on a verified HTTPS page. | To sign in without putting the values in page readings. See [Private credential fill](#private-credential-fill). | +| Path, name and size of a local PDF file | The agent attaches a local PDF to an upload field. | To attach the file. The extension gives the file path to Chrome. It does not read the contents of the file. | -Opzero Chrome stores minimal operational state in Chrome extension storage, including connection status, session/tab state, an extension instance ID, and pending update markers. The native host installer writes a native messaging manifest on disk so Chrome can launch the local host. +Pages that you ask the agent to work on can incidentally contain other personal information, such as names, email addresses, messages, financial or health details, or your location. Browser Control does not look for, extract or keep these kinds of data separately. It handles them only as part of the website content described above: page text, screenshots and DevTools Protocol results from agent tabs. -Users can remove the extension from `chrome://extensions`, remove the native messaging host manifest, and clear extension storage to delete local extension state. +## How the data is used -## Permissions +The extension uses data only to carry out the requests of the local agent that you connect, and to show the connection status in its popup. Data is not used for advertising, for profiles, to decide creditworthiness or for lending, or for any purpose unrelated to that single purpose. -Opzero Chrome requests only the browser permissions needed for its local automation bridge: native messaging, debugger access, tab control, script injection for the cursor overlay, download status, history lookup on request, tab groups, extension storage, alarms, and host access for pages the user asks the agent to automate. +## What is stored on your computer -## Security +### In the extension's storage -The native messaging host is restricted to the configured Chrome extension origin. The extension does not include remote code execution, external script loading, analytics, ads, or tracking pixels. +The extension keeps a small amount of state in `chrome.storage.local`: + +| Key | Contents | +| --- | --- | +| `NATIVE_HOST_STATUS` | Connection state, host name, time of the last check, reconnect count and the last connection error message. | +| `NATIVE_HOST_PAUSED` | Whether you paused the host. | +| `TAB_GROUPS` | Agent session IDs, tab group IDs, tab IDs and group titles. | +| `extensionInstanceId` | A random ID created on first use. It is shared only with the local host. | +| `PRIVATE_CAPTURE_QUARANTINE:` | After a private fill: the page's document ID and the CSS selectors of the filled fields. It never contains the filled values. | + +In `chrome.storage.session`, which Chrome clears when the browser closes, the key `opChromePendingUpdateVersion` holds the version number of an extension update that waits until agent work ends. + +These entries stay in Chrome until you remove the extension. The extension does not store page content, screenshots, tab URLs or titles, or sign-in values. + +### Files on your computer + +- **Installer.** The host installer writes a native messaging host manifest named `com.opzero.chrome.json`: + - macOS: `~/Library/Application Support/Google/Chrome/NativeMessagingHosts/` + - Linux: `~/.config/google-chrome/NativeMessagingHosts/` + - Windows: `%USERPROFILE%\AppData\Local\opzero-chrome\`, plus the registry key `HKCU\Software\Google\Chrome\NativeMessagingHosts\com.opzero.chrome` + + It also writes a launcher script in the `native-host` folder and the file `scripts/extension-id.json` inside the helper folder that you unpacked. +- **Host.** The host creates the private folder `~/.opzero-chrome` and a socket file in it, which it deletes when it stops. The host keeps no log files. The only messages that it writes to its error output are fixed text with no user data. +- **Screenshots.** The host passes screenshots to the requesting client in memory. It does not save them. The agent decides whether to keep them. +- **Recordings.** When your agent asks for a short recording, the client library in the helper folder (`native-host/transport.js`) saves JPEG frames, a `frames.ffconcat` list, a `capture.json` summary with the time and SHA-256 hash of each frame and, if `ffmpeg` is installed, a `recording.mp4` file. It puts them in a new `tab-video-` folder inside a private folder that your agent tooling chooses, with access restricted to your user account. A recording lasts at most 60 seconds (30 seconds by default) and stops at 100 MB. The files stay until you delete them. + +## Private credential fill + +Some agents use a separate local program on your computer to sign in to a site without showing the password to the AI agent. Browser Control supports this as follows: + +- The program sends the values to the host, and the host passes them to the extension. The extension sets the values directly on the fields that it verified. It returns only whether the fill succeeded. +- The extension and the host do not store, log or repeat the values. The host replaces the extension's answer with a status of "filled" or "not filled", and errors use a fixed message. The command-line client accepts values only through standard input, never as command arguments. The values are held in memory only while they pass through. +- A fill happens only on an HTTPS page, or on a loopback address with explicit opt-in. The page must be on the exact site that the agent bound, in the top frame. Each field must be visible, enabled and match exactly one element. The fill must happen within 30 seconds of that check. +- After a fill, the extension refuses screenshots, recordings, page readings and actions on that page, except the prepared sign-in submit button. It also refuses raw DevTools Protocol commands for that tab. DevTools Protocol events that the agent turned on before the fill can still report activity in that tab. + +## What we do not do + +- We do not collect data. We run no server for Browser Control, and neither the extension nor the host sends data to us. +- We do not sell or transfer user data to third parties. Data leaves your computer only if the local agent that you run sends it, or when a website receives what your agent submits to it. +- We do not show ads, use analytics or track you. +- The extension does not use remote code. All of its code is in the extension package, and its pages can load scripts only from the extension itself. +- We do not use or transfer user data to decide creditworthiness or for lending. + +The use of information received from Google APIs will adhere to the Chrome Web Store User Data Policy, including the Limited Use requirements. + +## Your controls + +- **Pause host** in the popup disconnects the host, stops every agent session and detaches the debugger from agent tabs. It stays paused, also after Chrome restarts, until you click **Resume host**. +- **Reload host** in the popup restarts the connection to the host. +- Agent tabs are in a labeled tab group. While the extension uses the debugger in a tab, Chrome shows a notice that the extension is debugging the browser. + +## Deleting data and uninstalling + +We hold no data about you, so there is nothing for us to delete. To remove everything that Browser Control stored on your computer: + +1. Remove the extension on the `chrome://extensions` page. Chrome deletes the extension's storage. +2. Delete the `com.opzero.chrome.json` host manifest from the folder listed above for your system. On Windows, also delete the registry key: `reg delete "HKCU\Software\Google\Chrome\NativeMessagingHosts\com.opzero.chrome" /f` +3. Delete the helper folder that you unpacked. +4. Delete the `~/.opzero-chrome` folder. +5. Delete any `tab-video-` recording folders in the folder that your agent tooling uses. +6. Remove any data that your agent kept, as described by that agent. + +## Children + +Browser Control is a tool for developers. It is not directed to children. We do not collect data from anyone, including children. + +## Changes to this policy + +When this policy changes, we publish the new version on the website and in `docs/PRIVACY.md` in the repository, and we update the effective date. The repository history keeps every earlier version. ## Contact -Questions or requests can be opened at https://github.com/opzero1/op-chrome/issues. +For questions about this policy, open an issue at . Do not include personal data, passwords or tokens in an issue. To report a security problem, follow the steps at . + +Google Chrome is a trademark of Google LLC. diff --git a/docs/RELEASE.md b/docs/RELEASE.md index e20a4f1..ba1f2c9 100644 --- a/docs/RELEASE.md +++ b/docs/RELEASE.md @@ -1,43 +1,93 @@ # Release -GitHub Actions builds releases. +A release has four parts: the extension package on the Chrome Web Store, the GitHub Release with the native host and skill, the website on Cloudflare Pages, and the store listing text and images. They are all built from this repository. -Release builds embed the stable Chrome Web Store extension ID from `scripts/extension-id.store.json` into the packaged skill so the native-host installer can configure Chrome without asking the user to copy an ID. +## Identifiers -The committed `skills/chrome-control` directory is also kept installable for agent runtimes that support GitHub path-based skill installs. Run `pnpm run build` before release changes so the generated `native-host`, `scripts`, and `chunks` files under `skills/chrome-control` match the packaged release skill. +| What | Value | +| --- | --- | +| Chrome Web Store item | `dcnjjnecbhipdbngkhjppkckpkellmld` (publisher `affdn`) | +| Native messaging host | `com.opzero.chrome` | +| Website | (Cloudflare Pages project `browser-control`, account `affdn`) | +| Privacy policy | | +| Store API service account | `cws-publisher@opzero-chrome.iam.gserviceaccount.com` (Google Cloud project `opzero-chrome`) | -Current stable extension ID: +Release builds embed the store extension ID from `scripts/extension-id.store.json` in the packaged skill, so the native host installer allows the store extension by default. Set `OPZERO_CHROME_EXTENSION_ID` only to build a package for a different extension ID. -```text -dcnjjnecbhipdbngkhjppkckpkellmld -``` +The store package must not contain a manifest `key`. `pnpm run check` refuses one, and it also pins the exact permission list. Unpacked builds get their ID from their folder path. + +## 1. Prepare the version + +1. Raise `version` in `src/extension/manifest.json` and `package.json`, and the host version in `src/native-host/host.ts`. The version must be higher than every version the store has, including rejected drafts. Check the dashboard's **Package** page or run the workflow with `action: status`. +2. If a permission, data flow or stored key changes, update `store/listing.md`, `site/privacy/index.html` and `docs/PRIVACY.md` together. The dashboard's Privacy tab must match the privacy policy. +3. Run `pnpm run check`. It rebuilds `dist/` and the committed `skills/chrome-control` files. Commit the regenerated files. -Set `OPZERO_CHROME_EXTENSION_ID` only when intentionally building a package for a different extension ID. +## 2. Merge and tag -Create and push a version tag: +Merge to `main`, then tag the merge commit: ```sh git tag vX.Y.Z git push origin vX.Y.Z ``` -The release workflow uploads: +The `Release` workflow creates the GitHub Release with `browser-control-extension.zip` and `chrome-control-skill.zip`. The reviewer instructions and the README download `chrome-control-skill.zip` from the latest release, so publish the release before you submit to the store. + +## 3. Deploy the website + +Deploy when anything under `site/` changed: + +```sh +npx wrangler pages deploy site --project-name browser-control --branch main +``` + +Check that `/`, `/privacy/`, `/support/` and `/support/reviewers/` return HTTP 200. Keep the `google-site-verification` meta tag in `site/index.html`. It proves ownership of the site in Google Search Console, which the listing's official URL requires. Cloudflare Pages redirects `.html` URLs to extensionless ones, so the HTML-file verification method does not work on this site. + +## 4. Upload to the Chrome Web Store + +Run the `Chrome Web Store` workflow from GitHub Actions: + +```sh +gh workflow run chrome-web-store.yml -f ref=vX.Y.Z -f action=upload +``` + +| `action` | Effect | +| --- | --- | +| `status` | Reads the published and submitted state of the item. Changes nothing. | +| `upload` | Builds `ref`, runs the checks, and replaces the draft package. | +| `submit` | Same as `upload`, then submits the draft for review. | + +The workflow signs in to Google through Workload Identity Federation. GitHub's OIDC token is exchanged for a short-lived access token for the `cws-publisher` service account, so the repository holds no Google credential. The trust is limited to workflows in `opzero1/browser-control`. + +| Setting | Kind | Value | +| --- | --- | --- | +| `GCP_WORKLOAD_IDENTITY_PROVIDER` | Repository variable | `projects/458096566052/locations/global/workloadIdentityPools/github/providers/browser-control` | +| `GCP_SERVICE_ACCOUNT` | Repository variable | `cws-publisher@opzero-chrome.iam.gserviceaccount.com` | +| `CHROME_EXTENSION_ID` | Secret | The store item ID | +| `CHROME_PUBLISHER_ID` | Secret | The dashboard's publisher ID | + +The service account is registered in the dashboard under **Settings > Service account**. The Google Cloud side is the `github` workload identity pool, its `browser-control` OIDC provider with the condition `assertion.repository=='opzero1/browser-control'`, and a `roles/iam.workloadIdentityUser` binding on the service account for that repository. + +Without the workflow, upload `dist/release/browser-control-extension.zip` on the dashboard's **Package** page. + +## 5. Update the listing and submit -- `opzero-chrome-extension.zip` -- `chrome-control-skill.zip` +Before you submit, compare the dashboard with the repository: -The workflow can also be run manually from GitHub Actions. +- **Store listing**: description, category and URLs from `store/listing.md`; screenshots and promo tiles from `store/assets/`. +- **Privacy**: single purpose, permission justifications, remote code answer, data usage and certifications from `store/listing.md`; privacy policy URL `https://browser-control.pages.dev/privacy/`. +- **Test instructions**: the short form in `store/reviewer-test-instructions.md`. +- **Distribution**: Public, all regions. -## Chrome Web Store Updates +Then submit for review, from the dashboard or with `action: submit`. Google reviews every version. Because the item requests `debugger` and ``, expect an in-depth review that can take several days. -After the first Chrome Web Store approval, run the `Chrome Web Store` workflow manually and enter the Git tag to publish, for example `vX.Y.Z`. The workflow checks out that tag, builds the extension package, uploads it, and requests publication. Chrome still reviews each uploaded version before it reaches users. +## Store images -Required GitHub Actions secrets: +`store/capture/` rebuilds the screenshots and promo tiles from a real session in a disposable Chrome for Testing profile. See `store/assets/README.md`. Recapture them when the popup, tab groups or cursor change. -- `CHROME_EXTENSION_ID` -- `CHROME_CLIENT_ID` -- `CHROME_CLIENT_SECRET` -- `CHROME_REFRESH_TOKEN` -- `CHROME_PUBLISHER_ID` +## History -Each submitted update must increase `src/extension/manifest.json` version. +- 0.1.2 was published and is live. +- 0.1.3 was rejected on 1 July 2026 for "User data privacy" (reference Purple Nickel). Its privacy policy link pointed to a GitHub file, and Google does not accept a repository page as a privacy policy. The fix was a hosted policy at `https://browser-control.pages.dev/privacy/`. +- 0.2.1 renames the item from Chrome Control to Browser Control, as the branding guidelines require, and drops the unused `history` and `downloads` permissions. +- The old `CHROME_CLIENT_ID`, `CHROME_CLIENT_SECRET` and `CHROME_REFRESH_TOKEN` secrets belong to an OAuth client whose consent screen stayed in testing mode, so its refresh tokens expired after seven days. The workflow no longer uses them. diff --git a/site/404.html b/site/404.html new file mode 100644 index 0000000..a17dcdf --- /dev/null +++ b/site/404.html @@ -0,0 +1,51 @@ + + + + + + + + Page not found – Browser Control + + + + + + + + +
+

Page not found

+

This page does not exist. It may have moved.

+

Go to the home page

+
+ +
+
+ +

Published by the Browser Control maintainers.

+

Google Chrome is a trademark of Google LLC.

+
+
+ + diff --git a/site/_headers b/site/_headers new file mode 100644 index 0000000..2ace6f4 --- /dev/null +++ b/site/_headers @@ -0,0 +1,10 @@ +/* + Content-Security-Policy: default-src 'self'; script-src 'none'; style-src 'self'; img-src 'self'; font-src 'none'; connect-src 'none'; media-src 'none'; object-src 'none'; frame-src 'none'; worker-src 'none'; manifest-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; upgrade-insecure-requests + X-Content-Type-Options: nosniff + X-Frame-Options: DENY + Referrer-Policy: no-referrer + Permissions-Policy: accelerometer=(), browsing-topics=(), camera=(), display-capture=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), payment=(), usb=() + Cross-Origin-Opener-Policy: same-origin + Cross-Origin-Resource-Policy: same-origin + Cross-Origin-Embedder-Policy: require-corp + Strict-Transport-Security: max-age=31536000 diff --git a/site/icon.png b/site/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..e33335d004f852254ab2b61b26019de3ab712866 GIT binary patch literal 3751 zcmaJ^XH*ky)7}Ikf>IR4N4la^5m1^55_~`*lz;-#rCF%bLN5u5fFJ}!q(}f23=o=1 z3y|^%Qltk07&<5jBE3m{*XPG~&iCVe=bYU+XXcuF?w#Ga_nN0BXkBhDK`sCQxKVmK zw?K{ibMYMl?)kQ}fDlR09t8l~fBZiHz+Vvn<{SV3o(=$~yt8U=YJiSI zP6oOUqx(c{TEK zVPXFLjF(r7qn&Piw3W$scDpD0^_zrBDypyjIG@gi9<6Jp5P6fEFP`NpPUFMT(e7E$ ziTw@o=Faa2WgoQ`Hfi13u1>bpIz&)i5aU?r6l zal?zOX`yC6$tG|$h}Lg2rhwpq@lLq;E1wv*Km`-e&K3q^a|?_ON)8TTH;bL4NUt<=$^bI)(LyVxy;>PgS?-lYulg2(!X8gSpAaE&&L#7eWV=j z63Fgg`{7k?70<77kMA%zW51;vbY01jgwbvN@GT;(;rl8wz!`fzse+O>Z$5p> z63$)Kg$#)(F7 z78wDWMqD`iD9j_$Nq4N{R$ru+^*0{85fK>U4fphn`GZnSOAGvinh~C5U(X-O2nY`& z0(FbSR^{vW9Q1bC4o-UuYbn3!HinDLq87-W9envp`0EM}v2jv*9(qjIN`6Xg*>DB@X*252r>AW%i& z;r@}Nx!=FD5AvXI<8<#f8y@l)@u(gQ%j->Mz6UJZ-I-;B&dm^%5Px;CAX^3X9TRB{ z2UCNqTbh;27sz`5RbDObw>I+=x3jUfiB~^Eh%1hU(XTiDZsZBCy6W9*G=ZWF=@ZY8 zmXdh1{7sW>Z(3jB3A!pDF}|BZe?*Q?lrOv#Azej1YQ=p7_gUB`3z=8W(d~Xa2z?H) zrNef`!3E=B^t03m>Cnc=k6Mi{_fKmNP84RB11<}LjNSB4*?(-V+F$Z|lSmUNEhFsb5`tRtkm@2AZ2%~cwkNuuu(=hHqdb@uM|r%huQWol1aE|pz} z8B~P6I7b!~g}B_0Ai+$>2nvKp!!SIcd^7o$57k#A$%L}IZC(g|XLd`6wfMgxd{sr} zB|!|hZOP{bre_&}`b60^K&E<*d4FNC zH5^735&(>pd8@>#3}U>e_X8@i&ec95Y_1^YheqzIL}C*|CaZ?VhCog(0Rq)_73&GB z^ht}+g{5h6x@Pm}Oo*l&%heK4?wK(u+e#|^7agp!xk*(@Mr@-wR`xUZCQ6%ecLrka z2Vl+N`9GZ@-8x$djy9puD5Ceu{swiWYe3X`#LY^1sjDV!04)Sn&R~2Sl^oL}23-BK zjpL$!olcS=d}`gPo7u!JCNDeJ;bYlWauB-<7O9LvJ6;7?zfN!YJNe9d?q?qowju`6 zQ_mevg^9OF55+FVP@Lx%y%K?{8SF6vi+#{(tkz`*I^Oon{`;??LnjPnm#*A7Z0oYq z&>~ivie3!yU_k2Yt*Zmni{#ZHx6RBf(^E_jumkl%<)LlaKASb;BRIRZ#|E`)>|8bi z4-w0MnXuM}vsM+@n1##?=EGf&#(&R!jaR)p5-~vLGI=n-@D+vLh~j4@kgecGdu=Pj zGXw%p7R5x+Z{b=%*q>5|NX{2FHhV$6MM@tNF@nD4pzTr@XC5V8IIHopN}m>3-~mU> zPHMy~y+2fKWdCLyh*G%#2>7&pxY&jm1M)ibc#PZ*jfW^}U!FC)>kqnm9X)oK5vS?z zKgR?_oIGlnrSZBU9S!zmktJ}U$Ti5cOmY!;-+O0J$)7FB>cu`omDj=Sdu(sRw95FA zA%~P4jd$B;_w8GM0)jU_^Q_O)=U-%XaMZE2o|g$EO0gAIzx#Y&_z=%I5yO^`keQF2 z2jrt#tEC;hLl8{j(V&)_+-qNl+(TM&BE1*OJ+C%IUUZDygICu3d1=BX1Xb(kIr{cF!vzqzD*^AJW2O8`D z#0wUm-Nk%0H5En35FX9NfqmpvX}h%*ssI_G{S3Arlcz7Y^QTZL6JKc9`So2YjK{&J z<++d8@KUoqk$N>tX_~;%1r^D5J#eklAPbwR2}ApChI2!r1$NgvVh~W7*M-ZoM(ziT zuL9rCu&@wcWXFafQhGDuZpgwcbu&PGyXo3_-j|aD5+}Ev)!DuIcRp;)d~1YW`{iXL zCThQ#=yj=i)R?wCJe-!3Hl(m>t5fC`a&i9HnY71lF)%vnU02p=RVl96itBso^P=OJ zd#(HrMoBapZSEJ|37IWUNu#qQN!#)S?jYr5^?{%ijNmrdblk-UwLhZLr<$gycqntP zh{IfsUQ|6Dra|N^R8^J%zJs)(Kw&5ZTNT{=H~rY@*lq-lg1`4UVx%j$ET>;)uV=(Y zA)z(DJtCt28@4`VRv5O2{BD|m$0`MCQey=FhnXq3KHrk&ggd7BHqChVxOCh0=;BT< z^qRVn%e5yq{{qX$KE&IPP!As3U^JH|vQzDg_XM>r-AcMhi0h5&|1f4TmkR!bPrpQX z4Zlmsv3C&n{xRA>)m=zI``!EaYuSs+)Q7_qHy4+AsSF|7omM?ss!vRxock>xKp_b| zUIeCs`Q~7axU|sEFi;EPkn)+m+nsua76J;u99ID&!;RFa3&)e5XG>;H?@T5f7;=O) zk7=mayj`n%9sgXlX$D(s23>}lnA&E6@iWD)pH9kI$lF$*IQ)!9b7%*eWKy|XYtGAN zE<_5ju=|{fFFpS-8UyX%q6?NP^>z!oWGZ6q|5=;j1pID%(>aVO= z>$b6L=$~#zZ!pm1SCJQq4|!M)!#9qXSVj6??cJN)zG{zgtu3u;dYORFIDYipZ;#Q6 zU(zPwcK-2h$XN=*&dJ&ThnRfC(--eVJXn`z*%#ha^+qj=b*Yd&S!8MvN~FBo7vWQPaC#2xtDqNw-42QT&B;{X{Z@r^Hiejj0FofnG-KW zfJ1bm*10LG*#<~svg-QlH3|B7dOF$pd1S}q>a6W~mGPOJqR}F_EMe3Qq^Dl{PyF&y z(kq0Z>3t@-Osz`u-S-$#iwC2WK#c>JuloV?+}tzB#O>pHNW#RRgevO0n>DW)Z2a3H zkOg`k!J$q*`YVQIoh(O)DcFeSC`hE;s_QCyHv?Q$8daZ?U5T;-+1`FllwEB=Sjq-0 z_nnVbQ3v-6^xv3!Do7AvYh*=$4 zvy(0`okS0FMhI#@vegt#Vb9Rb>!00{Y&!{MDJ%yB8s+$M67R2B37Ikx2#IF*!IBb|NZj6F+# z1$a%btW3-HPCI>^&PMaFui~~^D(K@AnJ3RUi D4}T1$ literal 0 HcmV?d00001 diff --git a/site/index.html b/site/index.html new file mode 100644 index 0000000..2b49ca1 --- /dev/null +++ b/site/index.html @@ -0,0 +1,117 @@ + + + + + + + + + Browser Control + + + + + + + +
+

Browser Control

+

Browser Control lets an AI agent that you run on your own computer work in your browser tabs. It is an extension for Google Chrome™ plus a small helper program, called a native messaging host, that you install on the same computer.

+ +
+

The extension does nothing on its own. Until you install the local native messaging host, it stays idle and its toolbar popup shows Disconnected. The extension has no server and makes no network requests of its own.

+
+ +

Who it is for

+

Browser Control is for developers and other people who use an AI coding agent, or their own scripts, and want that software to use a real browser window that they can watch. You choose the agent. Browser Control only gives it a local way to reach your tabs.

+ +

What your agent can do

+

When your local agent asks, the extension can:

+
    +
  • List your open tabs, claim one, or open new tabs in the background.
  • +
  • Keep the agent’s tabs in a labeled tab group, apart from your own tabs.
  • +
  • Read the visible text of a page and the buttons, links and form fields on it.
  • +
  • Click controls, type into form fields, and attach a local PDF file to an upload field.
  • +
  • Take screenshots and short recordings of the agent’s tabs.
  • +
  • Send Chrome DevTools Protocol commands to the agent’s own tabs for advanced automation.
  • +
+ +

How it works

+
+

Your computer

+
    +
  1. + Browser Control extension + Runs in Google Chrome™. Carries out requests in the agent’s tabs. +
  2. + +
  3. + Native messaging host + A small Node.js program named com.opzero.chrome. Chrome starts it for this extension only. +
  4. + +
  5. + Your local AI agent + Software you run, such as an AI coding tool. Sends requests and receives results. +
  6. +
+
All three parts run on your computer. The extension sends data only to the host, and the host sends it only to the local agent that connects to it. Nothing goes to the Browser Control maintainers.
+
+ +

Install

+
    +
  1. Install Browser Control from the Chrome Web Store.
  2. +
  3. Install the local native messaging host and the agent tooling from github.com/opzero1/browser-control. Both come in the chrome-control-skill.zip file of the latest release. The host needs Node.js 18 or later, and its default setup is for macOS and Linux. For example: +
    curl -fsSL -o chrome-control-skill.zip https://github.com/opzero1/browser-control/releases/latest/download/chrome-control-skill.zip
    +unzip chrome-control-skill.zip -d browser-control-helper
    +cd browser-control-helper
    +node scripts/install-native-host.js --extension-id dcnjjnecbhipdbngkhjppkckpkellmld
    +

    Keep the unpacked folder. Chrome starts the host from it.

    +
  4. +
  5. Open the Browser Control popup from the toolbar and click Reload host. The popup shows Connected when the extension and the host can talk.
  6. +
  7. Set up your agent to use the same folder. It contains a SKILL.md file for agent tools and a client that talks to the host. The repository README describes the agent setup.
  8. +
+ +

You stay in control

+
    +
  • The agent works only in tabs it opened or claimed. Browser Control puts those tabs in a tab group so you can see them.
  • +
  • While the extension uses the debugger in a tab, Google Chrome™ shows a notice that the extension is debugging the browser.
  • +
  • Click Pause host in the popup at any time. Pausing disconnects the host, stops every agent session, and stays in effect until you click Resume host.
  • +
  • When the agent finishes and cleans up, the extension closes only the tabs the agent opened and releases the tabs it claimed.
  • +
+ +

Privacy

+

Browser Control has no servers, no analytics, no ads and no remote code. Data stays on your computer unless the local agent you connect sends it somewhere. Read the privacy policy for details.

+ +

Help

+

For setup problems, troubleshooting and security reports, see Support. For a full walkthrough that shows the extension working, see the reviewer test steps.

+
+ +
+
+ +

Published by the Browser Control maintainers.

+

Google Chrome is a trademark of Google LLC.

+
+
+ + diff --git a/site/privacy/index.html b/site/privacy/index.html new file mode 100644 index 0000000..aed7dfd --- /dev/null +++ b/site/privacy/index.html @@ -0,0 +1,200 @@ + + + + + + + + Privacy policy – Browser Control + + + + + + + +
+

Browser Control privacy policy

+

Effective date: September 28, 2026

+ +

This policy explains how the Browser Control extension for Google Chrome™ (“the extension”) and its local helper program, the native messaging host (“the host”), handle data. The Browser Control maintainers (“we”) publish both as open-source software at github.com/opzero1/browser-control.

+ +

Summary

+
    +
  • Browser Control connects your browser tabs to an AI agent that you run on your own computer.
  • +
  • It works only after you install the host on the same computer. Without the host, the extension is idle.
  • +
  • The extension sends data only to the host. The host sends it only to the local agent software that connects to it.
  • +
  • We do not collect any data. The extension and the host send nothing to us or to any server that we run. We do not sell data, show ads, use analytics or load remote code.
  • +
+ +

How data moves

+
    +
  1. Extension to host. The extension talks to the host through Chrome native messaging, over the standard input and output of a local process. Chrome starts the host, named com.opzero.chrome, only if you installed it. The host’s manifest allows only the extension ID that you gave the installer. For the Chrome Web Store version, that ID is dcnjjnecbhipdbngkhjppkckpkellmld.
  2. +
  3. Host to your local agent. The host accepts connections only on a private endpoint on your computer. By default this is a Unix socket at ~/.opzero-chrome/default.sock, in a folder that is restricted to your user account. On Windows, or if you configure it, the host listens on the loopback address 127.0.0.1 and requires a secret token that it reads from a private file. The host does not accept connections from other computers.
  4. +
  5. Separate sessions. Each connection to the host gets its own session. The answer to a request goes back only to the connection that sent it. Events from a tab go only to the connection whose session owns that tab. A notice that control stopped goes to every connection.
  6. +
  7. Your local agent. The agent is separate software that you choose and run. We do not make it, and we do not control what it does with data. For example, an AI agent can send page text or screenshots to the provider of its AI model. Check the privacy terms of your agent and of any service that it uses.
  8. +
  9. Websites. When your agent navigates, clicks, submits a form or attaches a file, Chrome sends the resulting requests to the website, the same as if you did it yourself. A file attached to an upload field goes to that website when the form is submitted.
  10. +
+ +

Data the extension handles

+

The extension handles data only when your local agent asks for it. “Agent tabs” are the tabs that the agent opened and the tabs that it claimed from your open tabs. The extension puts agent tabs in a labeled tab group.

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Data categories, when they are handled, and why
DataWhenWhy
Tab details: tab and window IDs, position, URL, title, whether the tab is active, and its tab groupThe agent lists your open tabs (Chrome’s internal pages are left out), opens a tab or claims a tab.So that the agent can choose a tab and work in it.
Page content from agent tabs: URL, title, visible text (up to 12,000 characters) and the labels of up to 100 buttons, links and form fieldsThe agent reads a page.So that the agent can understand the page and choose an action. Page readings never include what is typed in form fields. They leave out password fields, one-time-code fields and the labels of those fields.
Screenshots of agent tabs (JPEG images)The agent asks for a screenshot or a short recording.So that the agent can see the page.
Clicks and typed textThe agent clicks a control or types into a field that it found on the page (up to 2,000 characters for each field).To carry out the task.
Chrome DevTools Protocol results and events for agent tabsThe agent attaches the debugger to one of its tabs and sends DevTools Protocol commands.For advanced automation. Depending on the commands and the events that the agent turns on, this can include the page structure, the results of scripts that the agent runs in the page, the addresses of pages and resources that the tab loads, other network activity and page events, and the page’s cookies.
Sign-in values, such as a user name, password or one-time codeA local program on your computer asks the host to fill sign-in fields on a verified HTTPS page.To sign in without putting the values in page readings. See Private credential fill.
Path, name and size of a local PDF fileThe agent attaches a local PDF to an upload field.To attach the file. The extension gives the file path to Chrome. It does not read the contents of the file.
+
+

Pages that you ask the agent to work on can incidentally contain other personal information, such as names, email addresses, messages, financial or health details, or your location. Browser Control does not look for, extract or keep these kinds of data separately. It handles them only as part of the website content described above: page text, screenshots and DevTools Protocol results from agent tabs.

+ +

How the data is used

+

The extension uses data only to carry out the requests of the local agent that you connect, and to show the connection status in its popup. Data is not used for advertising, for profiles, to decide creditworthiness or for lending, or for any purpose unrelated to that single purpose.

+ +

What is stored on your computer

+

In the extension’s storage

+

The extension keeps a small amount of state in chrome.storage.local:

+
+ + + + + + + + + + + + +
Extension storage keys
KeyContents
NATIVE_HOST_STATUSConnection state, host name, time of the last check, reconnect count and the last connection error message.
NATIVE_HOST_PAUSEDWhether you paused the host.
TAB_GROUPSAgent session IDs, tab group IDs, tab IDs and group titles.
extensionInstanceIdA random ID created on first use. It is shared only with the local host.
PRIVATE_CAPTURE_QUARANTINE:tab IDAfter a private fill: the page’s document ID and the CSS selectors of the filled fields. It never contains the filled values.
+
+

In chrome.storage.session, which Chrome clears when the browser closes, the key opChromePendingUpdateVersion holds the version number of an extension update that waits until agent work ends.

+

These entries stay in Chrome until you remove the extension. The extension does not store page content, screenshots, tab URLs or titles, or sign-in values.

+ +

Files on your computer

+
    +
  • Installer. The host installer writes a native messaging host manifest named com.opzero.chrome.json: +
      +
    • macOS: ~/Library/Application Support/Google/Chrome/NativeMessagingHosts/
    • +
    • Linux: ~/.config/google-chrome/NativeMessagingHosts/
    • +
    • Windows: %USERPROFILE%\AppData\Local\opzero-chrome\, plus the registry key HKCU\Software\Google\Chrome\NativeMessagingHosts\com.opzero.chrome
    • +
    + It also writes a launcher script in the native-host folder and the file scripts/extension-id.json inside the helper folder that you unpacked.
  • +
  • Host. The host creates the private folder ~/.opzero-chrome and a socket file in it, which it deletes when it stops. The host keeps no log files. The only messages that it writes to its error output are fixed text with no user data.
  • +
  • Screenshots. The host passes screenshots to the requesting client in memory. It does not save them. The agent decides whether to keep them.
  • +
  • Recordings. When your agent asks for a short recording, the client library in the helper folder (native-host/transport.js) saves JPEG frames, a frames.ffconcat list, a capture.json summary with the time and SHA-256 hash of each frame and, if ffmpeg is installed, a recording.mp4 file. It puts them in a new tab-video- folder inside a private folder that your agent tooling chooses, with access restricted to your user account. A recording lasts at most 60 seconds (30 seconds by default) and stops at 100 MB. The files stay until you delete them.
  • +
+ +

Private credential fill

+

Some agents use a separate local program on your computer to sign in to a site without showing the password to the AI agent. Browser Control supports this as follows:

+
    +
  • The program sends the values to the host, and the host passes them to the extension. The extension sets the values directly on the fields that it verified. It returns only whether the fill succeeded.
  • +
  • The extension and the host do not store, log or repeat the values. The host replaces the extension’s answer with a status of “filled” or “not filled”, and errors use a fixed message. The command-line client accepts values only through standard input, never as command arguments. The values are held in memory only while they pass through.
  • +
  • A fill happens only on an HTTPS page, or on a loopback address with explicit opt-in. The page must be on the exact site that the agent bound, in the top frame. Each field must be visible, enabled and match exactly one element. The fill must happen within 30 seconds of that check.
  • +
  • After a fill, the extension refuses screenshots, recordings, page readings and actions on that page, except the prepared sign-in submit button. It also refuses raw DevTools Protocol commands for that tab. DevTools Protocol events that the agent turned on before the fill can still report activity in that tab.
  • +
+ +

What we do not do

+
    +
  • We do not collect data. We run no server for Browser Control, and neither the extension nor the host sends data to us.
  • +
  • We do not sell or transfer user data to third parties. Data leaves your computer only if the local agent that you run sends it, or when a website receives what your agent submits to it.
  • +
  • We do not show ads, use analytics or track you.
  • +
  • The extension does not use remote code. All of its code is in the extension package, and its pages can load scripts only from the extension itself.
  • +
  • We do not use or transfer user data to decide creditworthiness or for lending.
  • +
+

The use of information received from Google APIs will adhere to the Chrome Web Store User Data Policy, including the Limited Use requirements.

+ +

Your controls

+
    +
  • Pause host in the popup disconnects the host, stops every agent session and detaches the debugger from agent tabs. It stays paused, also after Chrome restarts, until you click Resume host.
  • +
  • Reload host in the popup restarts the connection to the host.
  • +
  • Agent tabs are in a labeled tab group. While the extension uses the debugger in a tab, Chrome shows a notice that the extension is debugging the browser.
  • +
+ +

Deleting data and uninstalling

+

We hold no data about you, so there is nothing for us to delete. To remove everything that Browser Control stored on your computer:

+
    +
  1. Remove the extension on the chrome://extensions page. Chrome deletes the extension’s storage.
  2. +
  3. Delete the com.opzero.chrome.json host manifest from the folder listed above for your system. On Windows, also delete the registry key: reg delete "HKCU\Software\Google\Chrome\NativeMessagingHosts\com.opzero.chrome" /f
  4. +
  5. Delete the helper folder that you unpacked.
  6. +
  7. Delete the ~/.opzero-chrome folder.
  8. +
  9. Delete any tab-video- recording folders in the folder that your agent tooling uses.
  10. +
  11. Remove any data that your agent kept, as described by that agent.
  12. +
+ +

Children

+

Browser Control is a tool for developers. It is not directed to children. We do not collect data from anyone, including children.

+ +

Changes to this policy

+

When this policy changes, we publish the new version on this page and in docs/PRIVACY.md in the repository, and we update the effective date. The repository history keeps every earlier version.

+ +

Contact

+

For questions about this policy, open an issue at github.com/opzero1/browser-control/issues. Do not include personal data, passwords or tokens in an issue. To report a security problem, follow the steps on the Support page.

+
+ +
+
+ +

Published by the Browser Control maintainers.

+

Google Chrome is a trademark of Google LLC.

+
+
+ + diff --git a/site/styles.css b/site/styles.css new file mode 100644 index 0000000..c75f724 --- /dev/null +++ b/site/styles.css @@ -0,0 +1,385 @@ +:root { + color-scheme: light dark; + --bg: #f8fafc; + --surface: #ffffff; + --text: #111827; + --muted: #4b5563; + --border: #d1d5db; + --accent: #1d4ed8; + --accent-text: #ffffff; + --note-bg: #fffbeb; + --note-border: #b45309; + --code-bg: #eef2f7; + --focus: #b45309; + font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; + font-size: 100%; + line-height: 1.6; + background: var(--bg); + color: var(--text); +} + +@media (prefers-color-scheme: dark) { + :root { + --bg: #0b1120; + --surface: #111a2e; + --text: #e5e7eb; + --muted: #b6bfcc; + --border: #334155; + --accent: #93c5fd; + --accent-text: #0b1120; + --note-bg: #2a1f0a; + --note-border: #f59e0b; + --code-bg: #1e293b; + --focus: #fbbf24; + } +} + +*, +*::before, +*::after { + box-sizing: border-box; +} + +body { + margin: 0; + background: var(--bg); + color: var(--text); +} + +a { + color: var(--accent); + text-underline-offset: 0.15em; +} + +a:hover { + text-decoration-thickness: 2px; +} + +:focus-visible { + outline: 3px solid var(--focus); + outline-offset: 2px; +} + +.skip-link { + position: absolute; + left: 1rem; + top: -4rem; + z-index: 10; + padding: 0.5rem 0.75rem; + background: var(--surface); + color: var(--text); + border: 2px solid var(--focus); + border-radius: 0.375rem; +} + +.skip-link:focus { + top: 1rem; +} + +.site-header, +.site-footer { + border-color: var(--border); + border-style: solid; + border-width: 0; +} + +.site-header { + border-bottom-width: 1px; + background: var(--surface); +} + +.site-footer { + border-top-width: 1px; + margin-top: 3rem; + background: var(--surface); +} + +.wrap { + width: min(100% - 2rem, 48rem); + margin-inline: auto; +} + +.site-header .wrap { + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: space-between; + gap: 0.75rem 1.5rem; + padding-block: 0.875rem; +} + +.brand { + display: inline-flex; + align-items: center; + gap: 0.625rem; + color: var(--text); + font-weight: 700; + font-size: 1.125rem; + text-decoration: none; +} + +.brand img { + width: 2rem; + height: 2rem; + border-radius: 0.5rem; +} + +.nav ul, +.footer-links { + display: flex; + flex-wrap: wrap; + gap: 0.25rem 1.25rem; + margin: 0; + padding: 0; + list-style: none; +} + +.nav a[aria-current="page"] { + font-weight: 700; + text-decoration-thickness: 2px; +} + +main { + display: block; + padding-block: 2rem 1rem; +} + +h1, +h2, +h3 { + line-height: 1.25; +} + +h1 { + font-size: clamp(1.875rem, 1.4rem + 2vw, 2.5rem); + margin: 0 0 0.75rem; +} + +h2 { + font-size: 1.5rem; + margin: 2.5rem 0 0.75rem; +} + +h3 { + font-size: 1.125rem; + margin: 1.75rem 0 0.5rem; +} + +p, +ul, +ol, +dl, +table, +pre { + margin: 0 0 1rem; +} + +li + li { + margin-top: 0.375rem; +} + +.lead { + font-size: 1.1875rem; + color: var(--text); +} + +.muted { + color: var(--muted); +} + +.note { + margin: 1.5rem 0; + padding: 1rem 1.125rem; + background: var(--note-bg); + border-left: 0.3125rem solid var(--note-border); + border-radius: 0.375rem; +} + +.note > :last-child { + margin-bottom: 0; +} + +code, +pre { + font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace; + font-size: 0.9em; +} + +code { + padding: 0.1em 0.3em; + background: var(--code-bg); + border-radius: 0.25rem; + overflow-wrap: anywhere; +} + +pre { + padding: 0.875rem 1rem; + overflow-x: auto; + background: var(--code-bg); + border: 1px solid var(--border); + border-radius: 0.375rem; + line-height: 1.5; +} + +pre code { + padding: 0; + background: none; + overflow-wrap: normal; +} + +.button { + display: inline-block; + padding: 0.625rem 1rem; + background: var(--accent); + color: var(--accent-text); + border-radius: 0.375rem; + font-weight: 700; + text-decoration: none; +} + +.button:hover { + text-decoration: underline; +} + +.table-scroll { + overflow-x: auto; + margin: 0 0 1rem; + border: 1px solid var(--border); + border-radius: 0.375rem; +} + +table { + width: 100%; + margin: 0; + border-collapse: collapse; + background: var(--surface); +} + +th, +td { + padding: 0.625rem 0.75rem; + border-bottom: 1px solid var(--border); + text-align: left; + vertical-align: top; +} + +tr:last-child > th, +tr:last-child > td { + border-bottom: 0; +} + +thead th { + font-size: 0.9375rem; +} + +/* How-it-works diagram: boxes and connectors are plain list items so the + reading order matches the visual order for assistive technology. */ +.diagram { + margin: 1.5rem 0; + padding: 1rem; + border: 2px dashed var(--border); + border-radius: 0.75rem; +} + +.diagram figcaption { + margin-top: 0.75rem; + color: var(--muted); + font-size: 0.9375rem; +} + +.diagram-label { + margin: 0 0 0.75rem; + font-weight: 700; +} + +.flow { + display: flex; + flex-direction: column; + align-items: stretch; + gap: 0; + margin: 0; + padding: 0; + list-style: none; +} + +.flow li { + margin: 0; +} + +.flow .node { + padding: 0.875rem 1rem; + background: var(--surface); + border: 2px solid var(--accent); + border-radius: 0.5rem; +} + +.flow .node strong { + display: block; + font-size: 1.0625rem; +} + +.flow .node span { + display: block; + color: var(--muted); + font-size: 0.9375rem; +} + +.flow .link { + position: relative; + padding: 0.75rem 0 0.75rem 2.25rem; + color: var(--muted); + font-size: 0.9375rem; +} + +.flow .link::before { + content: ""; + position: absolute; + left: 1rem; + top: 0; + bottom: 0; + border-left: 3px solid var(--accent); +} + +@media (min-width: 46rem) { + .flow { + flex-direction: row; + align-items: center; + } + + .flow .node { + flex: 1 1 0; + align-self: stretch; + } + + .flow .link { + flex: 0 0 8.5rem; + padding: 1.5rem 0.5rem 0; + text-align: center; + } + + .flow .link::before { + left: 0.5rem; + right: 0.5rem; + top: 0.75rem; + bottom: auto; + border-left: 0; + border-top: 3px solid var(--accent); + } +} + +.site-footer .wrap { + padding-block: 1.5rem; + font-size: 0.9375rem; +} + +.site-footer p { + margin: 0.75rem 0 0; + color: var(--muted); +} + +@media print { + .skip-link, + .nav { + display: none; + } +} diff --git a/site/support/index.html b/site/support/index.html new file mode 100644 index 0000000..8a803d9 --- /dev/null +++ b/site/support/index.html @@ -0,0 +1,134 @@ + + + + + + + + Support – Browser Control + + + + + + + +
+

Support

+

Help for installing, connecting and removing Browser Control.

+

On this page:

+ + +

Get help

+
+

Security issues: do not open a public issue. Report the problem privately through GitHub’s private vulnerability reporting. See Report a security issue.

+
+

For everything else, such as setup problems, bugs, questions and feature requests, open an issue at github.com/opzero1/browser-control/issues. Issues are public. Include:

+
    +
  • The extension version, shown at the bottom of the Browser Control popup.
  • +
  • Your operating system and your Google Chrome™ version.
  • +
  • What the popup shows: Connected, Paused or Disconnected, and the text of the Error row if there is one.
  • +
  • The output of these two commands, run in the helper folder that you unpacked: +
    node scripts/check-native-host-manifest.js --json
    +node native-host/client.js ping
    +
  • +
+
+

Before you post, remove personal data from what you share. Do not post passwords, tokens, private URLs or screenshots of private pages. Command output can contain your user name as part of file paths.

+
+ +

The popup shows Disconnected

+

“Disconnected” means that the extension cannot reach the native messaging host. Try these steps in order.

+
    +
  1. Install the host. The extension does nothing until the host is installed. In the helper folder, run: +
    node scripts/install-native-host.js --extension-id dcnjjnecbhipdbngkhjppkckpkellmld
    + That ID is for the Chrome Web Store version. If you loaded an unpacked copy of the extension, use the ID that chrome://extensions shows for it.
  2. +
  3. Check the host manifest. Run node scripts/check-native-host-manifest.js --json. It reports a missing manifest, a missing host program or a wrong extension ID, and it prints a repair command.
  4. +
  5. Check Node.js. The host needs Node.js 18 or later. The host launcher looks for node on the PATH, then in /opt/homebrew/bin and /usr/local/bin, and then uses the Node.js that ran the installer. If you use a Node.js version manager, run the installer with the version that you want Chrome to use.
  6. +
  7. Keep the helper folder in place. The manifest points to the host by its full path. If you moved or renamed the folder, run the installer again.
  8. +
  9. Reload. Open the popup and click Reload host. While the host is not paused, the extension also retries the connection every 30 seconds.
  10. +
  11. Remove a stale socket. The host refuses to start if the file ~/.opzero-chrome/default.sock already exists, or if the ~/.opzero-chrome folder is not private to your user account. This can happen after a crash. Quit Chrome, make sure that no host is still running, and then fix the folder: +
    pgrep -fl native-host/host.js
    +rm ~/.opzero-chrome/default.sock
    +chmod 700 ~/.opzero-chrome
    + If pgrep lists a process, quit it before you delete the file. Then start Chrome again.
  12. +
  13. Use one Chrome profile at a time. All Chrome profiles share the same host manifest and socket. If Browser Control is turned on in more than one profile, only the first profile to start the host connects. Pause the host in the other profiles.
  14. +
  15. Wait for the version check. If node native-host/client.js ping reports Extension protocol v2 is not ready, the host is still checking the extension. This takes up to 10 seconds after the host starts. If the message stays, update the extension and the helper to matching versions, and then click Reload host.
  16. +
  17. Other browsers. The installer registers the host for Google Chrome™ only. For another browser, pass --manifest-path with the full path of the com.opzero.chrome.json file in that browser’s native messaging hosts folder.
  18. +
+ + +

Open the Browser Control popup from the Chrome toolbar. The status shows Connected, Paused or Disconnected. When the host is not connected, or when there is an error, the popup also shows the host name, the time of the last check and the last error.

+
+
Reload host
+
Turns off pause, closes the connection and opens it again, so Chrome starts a new host process. Use it after you install or update the helper. If the popup still shows Disconnected, wait 30 seconds or click the button again.
+
Pause host
+
Closes the connection to the host, stops the automatic retries and ends every agent session. Agents lose access at once, and the debugger is detached from agent tabs. The setting stays in effect after Chrome restarts. The button then changes to Resume host.
+
Resume host
+
Turns off pause and connects to the host again.
+
+ +

Uninstall and remove the native host

+
    +
  1. Remove the extension on the chrome://extensions page. Chrome deletes the extension’s storage and closes its connection to the host.
  2. +
  3. Remove the native messaging host manifest and the host’s socket folder. +

    macOS:

    +
    rm "$HOME/Library/Application Support/Google/Chrome/NativeMessagingHosts/com.opzero.chrome.json"
    +rm -r "$HOME/.opzero-chrome"
    +

    Linux:

    +
    rm "$HOME/.config/google-chrome/NativeMessagingHosts/com.opzero.chrome.json"
    +rm -r "$HOME/.opzero-chrome"
    +

    Windows (Command Prompt):

    +
    reg delete "HKCU\Software\Google\Chrome\NativeMessagingHosts\com.opzero.chrome" /f
    +del "%USERPROFILE%\AppData\Local\opzero-chrome\com.opzero.chrome.json"
    + If you installed with --manifest-path, delete the file at that path instead.
  4. +
  5. Delete the helper folder that you unpacked.
  6. +
  7. Delete any tab-video- recording folders that your agent tooling created.
  8. +
+

The privacy policy lists everything that Browser Control stores.

+ +

Report a security issue

+

Do not describe a security problem in a public issue. Report it privately through GitHub, so that only the Browser Control maintainers can read it:

+
    +
  1. Open the private vulnerability report form at github.com/opzero1/browser-control/security/advisories/new. You need a GitHub account.
  2. +
  3. Describe the affected version, the steps to reproduce the problem and its impact.
  4. +
  5. Submit the report. The report and any follow-up discussion stay in that private advisory.
  6. +
+

Use GitHub Issues for everything that is not a security problem.

+
+ +
+
+ +

Published by the Browser Control maintainers.

+

Google Chrome is a trademark of Google LLC.

+
+
+ + diff --git a/site/support/reviewers/index.html b/site/support/reviewers/index.html new file mode 100644 index 0000000..4fbc00b --- /dev/null +++ b/site/support/reviewers/index.html @@ -0,0 +1,171 @@ + + + + + + + + Reviewer test steps – Browser Control + + + + + + + +
+

Reviewer test steps

+

These steps show Browser Control working, for Chrome Web Store reviewers and anyone else who wants to try it.

+
+

Browser Control does nothing until its local native messaging host is installed. Until then, the popup shows Disconnected. For other help, see Support.

+
+ + +

What you need

+
    +
  • macOS or Linux, with Google Chrome™ 106 or later (minimum_chrome_version in manifest.json). Use one Chrome profile with Browser Control turned on. All profiles share one host socket, so a second profile cannot connect at the same time.
  • +
  • Node.js 18 or later on the PATH. Check with node --version. The host is a Node.js program.
  • +
  • curl and unzip. Both come with macOS. On Debian or Ubuntu: sudo apt-get install curl unzip.
  • +
  • Internet access to github.com (to download the helper) and to https://example.com/ (for the demo page).
  • +
  • No account, sign-in, API key or server is needed. ffmpeg is not needed.
  • +
  • Windows is not covered by these steps. A host started by Chrome on Windows needs a private token file that the installer does not create.
  • +
+

Steps

+

1. Install the extension and see the idle state

+
    +
  1. Install Browser Control.
  2. +
  3. Click the Browser Control icon in the toolbar.
  4. +
+

Expected: the popup shows Disconnected and "Install the native host to connect." Nothing else happens. The extension has no network code, so it stays idle until a host exists.

+

2. Download the helper from the GitHub release

+
mkdir -p ~/browser-control-review
+cd ~/browser-control-review
+curl -fsSL -o chrome-control-skill.zip https://github.com/opzero1/browser-control/releases/latest/download/chrome-control-skill.zip
+unzip -o chrome-control-skill.zip -d helper
+cd helper
+ls
+

Expected: the folder contains SKILL.md, native-host/, scripts/ and chunks/. Run all later commands in this helper folder.

+

The link downloads chrome-control-skill.zip from the latest release, v0.2.1.

+

3. Install the native messaging host

+
node scripts/install-native-host.js --extension-id dcnjjnecbhipdbngkhjppkckpkellmld
+node scripts/check-native-host-manifest.js --json
+

Expected:

+
    +
  • The installer prints Installed native messaging manifest: with the manifest path, Allowed extension origin: chrome-extension://dcnjjnecbhipdbngkhjppkckpkellmld/, and Host executable: with the path of native-host/opzero-chrome-host.
  • +
  • The check prints JSON with "ok": true and "status": "valid".
  • +
+

The manifest is written to ~/Library/Application Support/Google/Chrome/NativeMessagingHosts/com.opzero.chrome.json on macOS, or ~/.config/google-chrome/NativeMessagingHosts/com.opzero.chrome.json on Linux.

+

4. Connect

+
    +
  1. Open the Browser Control popup.
  2. +
  3. Click Reload host.
  4. +
+

Expected: the popup shows Connected and "Connected to the local agent host." If it still shows Disconnected, wait 30 seconds (the extension retries automatically) or click Reload host again.

+

5. Check the connection from the command line

+
node native-host/client.js ping
+node native-host/client.js getInfo
+

Expected:

+
    +
  • ping prints {"jsonrpc":"2.0","id":1,"result":"pong"}. The answer comes from the extension through the host.
  • +
  • getInfo prints one JSON line with "version":"0.2.1", "protocolVersion":2, "pageProtocolVersion":2 and "extensionId":"dcnjjnecbhipdbngkhjppkckpkellmld".
  • +
+

If ping reports Extension protocol v2 is not ready, wait a few seconds and run it again. The host checks the extension for up to 10 seconds after it starts.

+

6. List open tabs (read only)

+

The client accepts request data only on standard input:

+
echo '{"jsonrpc":"2.0","id":1,"method":"getUserTabs","params":{}}' | node native-host/client.js --stdio
+

Expected: one JSON line whose result lists your open tabs with id, title, url, windowId and groupId. Chrome internal pages, such as chrome://extensions, are left out.

+

7. Open and read a tab

+

Save this file as review-demo.js in the helper folder:

+
const os = require("node:os");
+const path = require("node:path");
+const { ChromeTransport } = require("./native-host/transport.js");
+
+const socket = process.env.OPZERO_CHROME_HOST_SOCKET || path.join(os.homedir(), ".opzero-chrome", "default.sock");
+
+(async () => {
+  const browser = await ChromeTransport.connect(socket);
+  try {
+    const page = await browser.open("https://example.com/");
+    const snapshot = await browser.waitFor(page, { text: "Example Domain" }, 15000);
+    console.log(JSON.stringify({ url: snapshot.url, title: snapshot.title, text: snapshot.text.slice(0, 200), actions: snapshot.actions }, null, 2));
+    console.log("The tab stays open for 20 seconds. Click it in the \"Browser Control\" tab group to watch.");
+    await new Promise((resolve) => setTimeout(resolve, 20000));
+  } finally {
+    await browser.close();
+  }
+})().catch((error) => {
+  console.error(error.message);
+  process.exit(1);
+});
+

Then run:

+
node review-demo.js
+

Expected:

+
    +
  1. A new background tab opens in a tab group named Browser Control.
  2. +
  3. Chrome shows its notice that Browser Control is debugging the browser.
  4. +
  5. The script prints JSON with "url": "https://example.com/", the page title, the start of the visible text, and an actions list with the page's link.
  6. +
  7. After 20 seconds the script ends and the extension closes the tab that it opened. Your own tabs stay as they were.
  8. +
+

This uses the same client library (native-host/transport.js) that agent tools use. It opens the tab, attaches the debugger, binds the tab to https://example.com, navigates, and reads the page.

+

8. Pause and resume

+
    +
  1. In the popup, click Pause host. The popup shows Paused, and the button changes to Resume host.
  2. +
  3. Run node native-host/client.js ping. It fails with Private client stopped; outcome may be unknown; do not replay, because pausing stops the host.
  4. +
  5. Click Resume host. The popup shows Connected again, and ping prints pong.
  6. +
+

9. Clean up

+
    +
  1. Remove the extension on chrome://extensions.
  2. +
  3. Remove the host manifest and the socket folder: +
      +
    • macOS: rm "$HOME/Library/Application Support/Google/Chrome/NativeMessagingHosts/com.opzero.chrome.json"
    • +
    • Linux: rm "$HOME/.config/google-chrome/NativeMessagingHosts/com.opzero.chrome.json"
    • +
    • Both: rm -r "$HOME/.opzero-chrome" ~/browser-control-review
    • +
    +
  4. +
+

Where to look in the code

+
+ + + + + + + + + + + +
BehaviorSource
Connection, reconnect and heartbeatsrc/extension/background.ts, NativeTransport
Request handlers (getUserTabs, createTab, observePage, capturePage, executeCdp and others)src/extension/background.ts, the api object
Page reading and actionssrc/extension/page-control.ts
Private credential fillsrc/extension/private-input.ts
Cursor overlaysrc/extension/content-scripts/opzero-chrome.ts
Native messaging hostsrc/native-host/host.ts
Command-line client and client librarysrc/native-host/client.ts, src/native-host/transport.ts
+
+
+ +
+
+ +

Published by the Browser Control maintainers.

+

Google Chrome is a trademark of Google LLC.

+
+
+ + diff --git a/store/assets/README.md b/store/assets/README.md new file mode 100644 index 0000000..fcf413c --- /dev/null +++ b/store/assets/README.md @@ -0,0 +1,53 @@ +# Chrome Web Store images + +These images come from Browser Control 0.2.1 (commit `9b0fcea`) running unpacked in Chrome for Testing 149.0.7827.55 on macOS. The session was driven through the native host socket with protocol v2, the same way a local agent drives it. The screenshots were not generated or mocked up. The only edits after capture are a crop and a conversion to RGB. The page data is fake: a local demo page on `127.0.0.1` and the public Wikipedia article on web browsers. + +| File | Size | What it shows | +| --- | --- | --- | +| `screenshot-1-popup-connected.png` | 1280×800 | The toolbar popup open over an agent session. It shows **Connected**, the Reload host and Pause host buttons, and **Version v0.2.1**. Behind it is the **Browser Control** tab group with the demo form partly filled in. | +| `screenshot-2-agent-filling-form.png` | 1280×800 | The agent session tab group **Browser Control**, which holds the Wikipedia tab and the demo tab. The agent has filled the "Book a demo" form, and its cursor overlay is on **Request demo**. The debugger infobar is the one Chrome shows while the extension is attached. | +| `screenshot-3-deliverable-group.png` | 1280×800 | The finished result. After `finalizeTabs`, the submitted form ("✓ Demo requested") sits in the **✅ Browser Control** deliverable group. The extension closed the Wikipedia tab and detached, so the debugger infobar is gone. | +| `screenshot-4-observing-wikipedia.png` | 1280×800 | The agent observing `en.wikipedia.org/wiki/Web_browser` in its tab group, with the cursor overlay on the article. The page was opened with `?banner=none`, so no fundraising banner appears. | +| `small-promo-440x280.png` | 440×280 | Promo tile with the shipped icon (`src/extension/images/icon-128.png`), the name, a tagline and a real crop of the ✅ tab group from screenshot 3. | +| `marquee-1400x560.png` | 1400×560 | Marquee with the icon, name, tagline, three plain facts and a real crop of screenshot 1 (popup and form). | + +All files are PNGs in 24-bit RGB with no alpha. The screenshots have square corners and no padding or borders. + +## How the screenshots were made + +The scripts are in `store/capture/`. Each one reads its settings from `env.sh`, and every path can be changed through an environment variable (`BC_TMP`, `BC_BUILD`, `CFT_APP`, `CUA`, `NODE`, `BC_DEMO_PORT`). + +1. Build a clean worktree of the release commit into `$BC_BUILD` with `pnpm install --frozen-lockfile && pnpm run build`. +2. `serve-demo.sh` serves `demo/index.html`, a fake "Book a demo" page, on `127.0.0.1:$BC_DEMO_PORT`. +3. `launch.sh` does the following: + - Creates a fresh profile. + - Writes `NativeMessagingHosts/com.opzero.chrome.json`. The manifest allows only the unpacked ID, which `extension-id.py` derives from the extension path, and it points to a private wrapper that sets `OPZERO_CHROME_HOST_SOCKET`. + - Pre-pins the extension. + - Launches Chrome for Testing in the background with `cua-driver launch_app`, using `creates_new_application_instance`, `--load-extension` and `--disable-extensions-except`. It also passes `--disable-infobars`, which hides the "Chrome for Testing is only for automated testing" bar. + + The launch never activates the app. The script checks that the launched pid is the expected Chrome for Testing binary, because the bundle-ID route can resolve to a different install. +4. `drive.mjs` connects to the private socket, checks `host.info` and `getInfo` for protocol v2, and then runs commands that `cmd.sh` queues. The run used this sequence: + ```sh + ./cmd.sh '{"op":"open","tab":"wiki","url":"https://en.wikipedia.org/wiki/Web_browser?banner=none"}' + ./cmd.sh '{"op":"wait","tab":"wiki","text":"web browser"}' + ./cmd.sh '{"op":"open","tab":"demo","url":"http://127.0.0.1:8765/"}' + ./cmd.sh '{"op":"wait","tab":"demo","text":"Book a demo"}' + ./ax.sh press "Wikipedia - Part of group"; ./cmd.sh '{"op":"move","tab":"wiki","x":372,"y":560}'; ./shot.sh screenshot-4-...png + ./ax.sh press "Northwind Analytics - Part of group" + ./cmd.sh '{"op":"fill","tab":"demo","label":"Full name","text":"Jordan Example"}' # also Company, Role, "What would you like to see?" + ./cmd.sh '{"op":"click","tab":"demo","label":"11–50"}' + ./cmd.sh '{"op":"move","tab":"demo","x":990,"y":624}'; ./shot.sh screenshot-2-...png + ./ax.sh press "AXPopUpButton (Browser Control"; ./shot.sh screenshot-1-...png; ./ax.sh press "AXPopUpButton (Browser Control" + ./cmd.sh '{"op":"click","tab":"demo","label":"Request demo"}' + ./cmd.sh '{"op":"wait","tab":"demo","text":"Demo requested"}' + ./cmd.sh '{"op":"turnEnded"}' + ./cmd.sh '{"op":"finalize","keep":[{"tab":"demo","status":"deliverable"}]}' + sleep 5; ./shot.sh screenshot-3-...png + ``` + `createTab` opens agent tabs in the background, so `ax.sh` selects the tab that should be visible. It presses the tab through a background accessibility action. `ax.sh` also opens the popup the same way. +5. For each screenshot, the window is set to 1300×820 points on a 1× display with `cua-driver set_window_frame`. `shot.sh` captures it with `screencapture -l -o -x`, which doesn't activate the app and includes the popup as a child window. The script then crops the inner 1280×800 rectangle at a 10-pixel inset, which removes the transparent rounded corners without scaling or padding. It also handles a 2× display: it crops a 2560×1600 inset and scales it down, but that path was not used in this run. +6. `cleanup.sh` stops the driver, the Chrome instance (by its recorded pid, and only if that pid has the capture profile) and the demo server. It then deletes the profile, host directory and state. + +## How the promo tiles were made + +`render-promo.sh` crops the real screenshots and copies the shipped icon into a work directory. `render.mjs` then renders `promo/small.html` and `promo/marquee.html` at their exact CSS size with a headless Chrome for Testing through CDP (`Emulation.setDeviceMetricsOverride` and `Page.captureScreenshot` at scale factor 1). The `--headless --screenshot` flag was not used because it never writes a file in this build. The output is converted to RGB with `ffmpeg`. The tiles never use the word "Chrome" as display text. To use a different icon file, set `ICON`. diff --git a/store/assets/marquee-1400x560.png b/store/assets/marquee-1400x560.png new file mode 100644 index 0000000000000000000000000000000000000000..48e537ea4409a2ce8fd49fc635da16d88d25fcdc GIT binary patch literal 238709 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

c^8fe7&6$%}p2?UA`|n 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 Q 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%0De=faN_QYMTk+GX0UP({C^aD?GTlRp%ITy?ku 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 literal 0 HcmV?d00001 diff --git a/store/assets/screenshot-2-agent-filling-form.png b/store/assets/screenshot-2-agent-filling-form.png new file mode 100644 index 0000000000000000000000000000000000000000..91cc2e1b1f9799d68f99fa2b8529b8cc206a13ca GIT binary patch literal 110878 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

J%2 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

V=&>NYswK9n6|A5*w4Ts02|>G3ev?*m%MF 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 literal 0 HcmV?d00001 diff --git a/store/assets/screenshot-4-observing-wikipedia.png b/store/assets/screenshot-4-observing-wikipedia.png new file mode 100644 index 0000000000000000000000000000000000000000..ac13ef5b0cf7dbaaafefc0c2a72e18ecc782909c GIT binary patch literal 241703 zcmY(r1yt10+C2;iNJxj$EgjO`-6AC|-Q8Uh(%s!4(%lUr4bolGAvqHN!+r1l*7{hw zW{_d#7th(x-uv0daAid)6eN5kC@3ft8EJ79C@47a^c5)r40!kp(fke`5bdQuJAucS zFKDP>kGC)1*qUg{n99h|K_P%|UO`!eqJW=4L9v3rP|&dddH(WN(Eoi-`s(EeFRu)V ze+`8Rp6P(UFuAY(zgNEc@AH>e+Jc{nd=it90gs=I98FAYoy_f=`CuOYfp?U$5EE0D z5fdX-cC<6Kur`5$a?kXMZIgW^f*sOjPAMrN&hQDg9>Xw}5+V7+*DzeUc8QurYun!U zsBQG*3sx4pSYcn8(dAAUVMRo|VU~sQQI68Y71}qtH=pnJbNbR=`2-cKxT2T-=BI9J zMjx}WkrHDL4wo)UWo6VTLNSsc_ddLx@g~i^_LaPa_&lHC(P8V#T`ADn(hda)%P(-B zC+<;fB1(l|3eM@Ap116IhgL16HCRjDsk{FwPlekw+tlnFV!pMXTWb_N7)X@Ab`iQvb*yJ`^gEX)NU zPgbk9+Ha?y5r1Ty)of|cmdu%zKtZ8H$%u=nx@R6|!MS74&xdmTkUrl~K{!2K!<~nL z`&K`obsU4^R>G(ZEvsFq8Gx^>^$nXIePxvcT?cb(J)`BsxodUIG{G8kI`eiU!OV4M zyv5ho>1@S;Mzu7SOl|A%@NjeUOG<8FP>`6I*a>DLX(9IEoYiEh%#8OvGG!B46^#ay z{!;Uo$KRD1HeJSBI~D`qQn4KiKoV~MrYWMf&tI1yf zAtv>)tCt@2D;A2p2%}zm|40IL{4${kI;=<-TA3+RCli*A~Q4d zQ%zRL zNHyGFy%_THlnHmE6k%AAYGz#6>Wqwxm>4vafQSfWp}yW;b5vphPi#!gP{EPaRRJH! zRd#l^_4(i3(G(F8kr)yo)4m(wW5aNX%&e2|4~Q(jFbjQs1$rHxbLDEIsUIkW>{gl` z9wFB#1f2Zt=iQySNTQixWzohH+1pt^RudLdu~G1w^*Z2x?~77U&kjxqJsLO;<6c)N zVxcflLYqxw3VPkh>%0!=YhTU5op`%@aEgw5YR)1qDM5Q}#S-6xusz9u*W1qDp``2J zMf_Sr1pPSBqsr{3cJXVvR;-SIAkzrxVd^1IIDV#P-|8CEOyzeHH zh1txxd3eY^Pj$fCL?#NicXW_gF1(ZOWei6w@yixoj<|!x7ev0H>YdkM-tLV;M&E?W$!e^(Ru%gf-g=2`PJH86>>U zf55}_;T)ge(?b*i*OgfPFL3O|Cedyia}^qB2nfyY7x}(#y;3vZ^c$hyNvv68WS=VL zed)nKFnEpjHYIFWS{K4=E`Ag|IZ4BOo8nR1n$?;tJ-A%+w_LNv(7<3LiC*K_P!sc0 zfn?NZ3iEoa%jn?W(PTyfJUox9KU10$R~Hww8b4N&8MQG@b4d|I$uFM?If{5L-dgNM z-p(j(?AnK&XeOBrM&hZF4t5|zUt%St@;I47S~NqsP7K3wj=7(C)}YlZwHmDEf8e5# zg<}kYqVc*tZ6F@j(D!k<+!<+c+Aq)Hwy{~NKUi&Z(}|ku;kmmUWma%qYxS^t2)-Ef zYHx4Xs<(Ey8~*ap;=V2?9j>W!eK?-+ckTBdb~Jmb2+-XS+<*z@p-(SXCEivvejvE zEb{f(9*nY@D^Eg)UI91MXejzih;K@;6kQQ|++ZIq13wLUd#c@db=bL4lb!L!6>3DJ zeHg?mkS43c$(ifW00AXN`pANc##6xO-$2CMzk6e#LfTJ~7i#``9#uA#O3BdrV=Zhq z*{1RSCLBT?yaqq7n1wR#X@1*GON0OPkE#@o?wRrYW%${q93xR62(hr}cs|PlQ8y zGgCWK`^6xE#Z0kohi5})Xm1GV!{hBb)ZGx zk4xC1&>b4(4LgPCpGAQg73FrM&%=(?*VeNH3$}$IFGwHk&ifyDJLyI>gKkyO|L04e{caMaGq@A3; z!?BR_OZd!pey)KoXJ=<|C~d_4fq}0x-(d~k%55n$S}(rTeMI={+_V0T=f^WYec$`t zljWup#}oj1l>q0CepiBvmc?O(%i8Sw2sxHQEC|hDx5(E+6a=UJJ*aykN67E_@%F<6 z<)U07O@LGEdxdnfiR{f*-FEkj1SP>d>Tcb=;w(NlVnOe_?V*@|R|kDOV0FzixBESJ zBB`&xSCEV$&Jy(TR9RbJ_XCx4_{(%GeN%qBGn3cl=eNI%lmfG?YWay={v_QK{pUQ+ z0262i?=N?&REiGOPZpoIrD7mAkGT%p@2%ARgskrnkapUcyB@4=e|%r8ZLnT!73m8B z*!px3O7dR*o3E>&(bBB<{of{^3SDVw>9g#!35aoHP|%n0ddMHcR3Df z?atPc=u_6)9SUq$Jq5gOd&<_j)}L>q2v%2DpC6o-(@A_UZ*J6YbUM8cc1Exu?hNV< zPfbtgO7RXyP9X005{S(ZNB~Syj^u`d5B={Y<4;N4LV|VsJ$TM4+VbO2cyAQtIX%15Q*g_ z06MuMukbs5rLp;)_q+}W2&j3#_d~ZWtj!WL0*77_xk_a&S3w7I(zL#y*=)bL)L=E& z7}%H|V%r;x2o|QrY$=9gg!ZQlbN-ght#9u>5jPINepjo&#oO83bl4v!^2v|&eVe`o z$i_#oSdOZ?)@qETzSmUq)#HGEX)0!6;ECcJ?{L!*5yUyQxUumL8#7)g;%pEjp=~0k z!Sp$sVa?*S923Tee2nL*YgRXY%Lz#{Ws&39#Rkz9!FRSDfRL!&lIUFDvxK# zQB|tz;Ts}ec7SZ4XR`VZFXxH9{)uRVe18YvB*9@YMVV^3$xbO`t;0)WLYdWPIF<}n zLeO@hIzTZwD+gfk_0fD8Uo-tIU->Jz5ETX9v-PeVEO=DH(oV4vi)KPdh2HP*hM{?* z!=ocPwcZF}*XcWr@0xLzYT(Ad+5WB5yISVoe>HebkWeCDESg7#m?rkAM3HLCX@9)v zO9_ioHh-F8u%v%!!=J$G8!dnU_OAW}l3}J0SGsUK#3w(#72&A0kOYQ)gFYhur^K+}28(c>( z=86Q(W^_CMxN7d{=>g=B%{)fV2&X$$ylKA8&CW+FQAAEm=L7#JfEo_FHScbJXcFH$ zvZ)@40=r@gXGh20+ofg)int=NTHgp;-L|QQjqK?G$GKvKjM<7+z|gchy?HvGKoLZL z$|!}%hGEe$4`W|{x`z%=7a58sme9~?^E?S9`R#gyTWLd1PI2}cK3mAmc59~$)V1W8 zV6&g!;T4{n?Rpxg3+oe><$ZU+VZhpEmchiV8VHN(N8N&2N4y#R>$CVEGF=?nv`nq$Bt`_BO5i zMpT#ypc67v8ZOqFIP^P+-JUE&DQpH^+!algZtatJqHhi@t|v z@N7tcDkxG+a`A=p@7|2_ossI_Pf)Qlw>a+R4{-ZFaQ|Q%!|-(tfPu4jwYZpMgjK9& z-(DfQFt{_rIRYea3d-;#41ILE<~5;hh&)@b>mL zoyd-Q)9pLU+<9lE|upe(iR$G(c=;XBW@A zq1gR1i5~PlIiAAY{H$MY@eXvcp!I!>d9Rph{@r(k!W)WpYil5ah|ksgtmBqjL`e2? zQIiCoN(>DFHgQ8vywc*`UWVPDFQ9!YSmaSA3}_B3!W7b?A|Rx4*%2p&sYF4Yu66J| z^1;38kP0|h?|P=r@ivrW)jGall+>T=KVGb3)^23*WF5WO4%7Z6Ok7bB_Vf*hK8%$J z7U!Wq9E+V*m0mtyr8G1g+w1952s55fY3o*d@kEkuSbiT~;yQ$$HhO6X7*X8uH|4;zquQEN4i6U_U}!dJL-J8qCGnP;DW zv03+*`(5^%?FB(b1F}hq+1vqro;SY^WRvKeHY4^YD99}JW#?Mk0u-wn|lKoab85!1!BiAM9Z}$35=^bbh*0x?B-Wv~Z z>>ebsnK8Lk`WDogjl*`hPZgNiS$^*HzIQ!c@ZGyQP$}AGY2j7OC5vkVny6Bx2+8;F zY}u!{p0`Xo%2H)dzeD|S+?#Od)t#N3`g(S~36&2SPS8A!_Q$iVu~H6ZN)iO?OvgmO z9y%4a%!8I~@jaPYU$4$o0Sj9GA2@kzPV_f9J~wz8Wxq3W6Ok29vC_M`gxq>A0H6Ck z7)1coeHe1(450kzIG<~cfz-=Pp9->8mLvBm{xX>?S8YxpWU(lppC;tz%TW#w1O@qT z2pOTWw=E$gH<#2_!Se=GfnpYKee3W%0IW3FuMMzYb2;>5Vsj_?n~wqIruba0QT4RZ z^U8_h!(eV1#g-frDW`nzM03zpyA;YGf4=@aJKVu=YsMmctS zoU|Gq>LuA8tY$||?rWpfu{0<_LHKm8)x`L`b>iWel-&2Fu#CFd1qItXv-TQ!k|&w`SG!_s)tl+{T(3dgA>eeug1jx4NSCA>qk z+ZjWu9hor%xshZXEE)JxYHAt|#fLo>dNdX9`VeQuYyIh}-iO<#fcu)ZAS?jcP0?Q{ z)yyNu=;7#n&F%ar_8TgBGOv{$R4E348Lz897;{mFRTDKOhgm#W9S_i`XgC2V*IFDY z=(GM@6SuoiwAxNSZSP0dd(`cpzimJ^kjo5gcB%!FRO?9tNuY^~!@=p#_X z%6y=2_!#(IK?ZStY6#1#AdIF|g>|_c?#LM1XSGhnSnzht?AGzrtsks>aLeWg8bIXs zbn)+7>L$x6`7YYG`1$+hn(m4_M;Si*ipbj*A3hthc&;T<$^D*GMov(^B3Ny8!K#>D zI#Uf@@wi-T_n^3dWf?aFlmpQDvS6mQ!xsWTrkYtpVhwj?*5P?=P9x1;cyPMf7Uy?Y zCi}1BX2}ZvghS)eyCF0gT|@KaMUno{X^{_{drQ$zUF<`nSxt)Hor{PV)JPaD;+3S8 zB_s6My zA%idr=M$fxj#cSdjOb1U6&u6LHE76e90I6pD??B)3;=E=)aBCBvpw2^M&NIG-!vhsWuxc|*9!_|2Y)Yh1ag zBT4!cIGw)fm=f@nKi}0OTEQ#q;nl-&s15@=&vPPT%?h-Lma49Mi8Db(A|WDgdSrB-z#AQB7m|~&$l(M6n})Hb2zZz-Y2vgONu}n;Rjb8g_d2J zb8`qAFPzMzi;sghSD$u6=&mGW3o}T z@>R=Rgr4PV&V&qdOH%gZC_-b1_?D`kp~UI|b#(E(s<`WVZgjXTL>$%d)z1@^U~lBb zr}wzrYw&=Q3x@X>&VMtjuJ$h-W0r1PqEZ4|q=H<4@P~`t@kQ0Mv-7v0d*Rn*uQR zPJg=AM2-XQBS+)f+S=Y}vu8JS7z)3aNk(+5#_39nQWmetBtBBZ(r;jf0O>9|k}2TD zsUQUuG_XT&fkojM#x{k?Pkx92zGjb0mDi!k!5ClNBlmx6cI~0g` z(KiDZ1p)qg26jud5TMBn@FlRA3AuFVE!bD<^RZQ(DB{e3Lk>~eriH+tYp^}I~-!y*REyr!BIeyn`s=TsKC4&|`=7m{;K}+-V^Tu;Z9Bn;6Z&~F%y(6ZY zR^Md-8*a`{ImfWtep57k7x?NQdyKLuOecvE4;U*gwG@==CYbcvrHw?3NPl`pL4LWftAh!@MR#wUbDq&xbnuUGb(Ru4Mu3K^UTMxM1mrkAsP z)qvf7ZM|44q2EAL!@;u4soQ!LLoD!M^yw;4m_}`z>quvHMK*=04u}=Be>Y-`)(h27 zenFw{>OU8MqE@=Ry8M|&pAuTK{~d4U@9sp79)WxYuStB4e%v0TR$`U%my3KmA|gWB zWCjl7^XJ3gl^Vknc8{(4=gO7eIijF(=+)8%ZDgOHULksSbv+*|31#z}XvrsQQtB?% z12ILZA}IKL#@5Tdl9xv&^mN{r5ruzpZ*>bCO{BL>ndj{FFfKNC=bQRI*En-{LmgTT zmfR~S4ZCeXMK=12mFcaqnWLe{y~blRRjqh)3>Y+6{CM2)e?zT#=M@F4d^uRHkRgqw9J`F z8zh!U=6ZdUF~-GW zM*6M7q2NynMXb$^bero5jm5gpEfF~`hxE-R0HTcw?-$v8jsUM{qnB?-|HkkYnK zluNCF9A_6s-^xA!+1@W#Ib-@^q3h;hu3f_Ea2QOocuIg%SLn~EG!j<}oT{FH9^&iR zsdqc(pvV64xhg!JuOb;CNPtow%~dhDl0dQP>Fmg62&6)Iwt2$o2KAW#nLRt$2qv_& z<9AnbfYetZV*zy3&(FbCx(qeQvahJ&>!-jBr0=W;nuy<>MUl+wkJLfV6;dG*Pc z(}fz~!{Bo_!z)C6*Xw8nZWI_yQCHfZ&GMH5grHDAC|VE%vUQ20IJC{LE3T*p7$v}H z+D3@q8%%S&J=MLYodS*x^&qwb+1f^fvilX9Xlayq7W)U!9fXDe-Yia|_xOD~N1w$) ze1d=5EVoh(bgCV30t16fJVw=hZ$h6`YCMzs^=$h@i?QZ$p;Oma|jv=K~cC#Dm)D&8$y_)U!#^do++}t#Jv}W`O8!z6M4Thch z08j%>mUEWlnLLf5{webRa=TdMm0b>JKUP8$KLd}P*X1zyt=xc=H{CNIKY!-livl|q zZ^&nt?=4iw9kE=jrHW==n)AMgFFXrI#1aJNrYN@$q+aE-j>Lug!`%lyX5Cif2Xgo= z=&4}~LQy+P3`nUk%Lny2Ivk%yw0T}+Otc8IMtCK zPA@LXei>Ol(8!9cN>M9ia8`l_{ytRIErx0qu8>)%EM8S!4cbW54f92lgkw>c|Iz7k z70@_+zWo6kp9=q3;X+xb*$^-IFx#jj3=n($GW_6Jh!CT^ zFw_tT*}DGiT|$iNwPNYsJJ3pawUmf7_z$+Jp_AQ&d*|J|Rc z$TJAKS(yI$hUV*qDj`eun-;acp`lPF1trObPc}z6?`nokQ5rf72avvB-`sHO*{H29 z_4Ngr>Iz~%lz(~J72B?&Sjt-GwBcOGd$hnSc*J8hPQAaNK4fIx_JBQ}ZF=ilC%w+3 z)_V2xOry*FX=P?qPKczb$>T4s2(zC1BO9x7f$13j5|{4S#cyLex55?Ek$4HT{(P!+ zCcSd~QZ$AVg$&dL^DuKKd0W+XaYR%ykH8y@|9JtPQ|Fdw1h_Ljdd_ESK0Cv4mT*~s z0d+p!EW4i!GcJp1=I1?UmgGIJl#2W~m}v2xHh4954kmqn-|PBNujKAFtZ8m~5LC%V z5ma`L2O6;0PDJgqV7>MEr-4%R(IsVSg)^afA^O6R<9sue1poOMpvaQ)=qJ2-i|NB% zV@)ulTj~zEUH8ig6rY-=FtIHZA&`=IHG@hZbTfCx>V9s((KqvOy+PUc;MH{@x1foD z9OLXD3A>HQkA&>x>Z}^iy@DP$z&AD?VWvHff3&r8e0q%eC2Y%>o-FOklh@C(G)7l@ zaU_H|@IEwaq!{`Ye4$cKHjLfHsv^XModp{IHY98f_)SwAq)pBf;(l<5ZW3MFxF|t4 z<`!Tu%P!K)-sqOA?&6Ts3Zp)_@?%3%24@YmkRGER`P@!RajLB!*b~Xa|1&mXf40M5m=oLfn7n#7 zDhI}Yyma^uM-)v+yA2z7_BbcGm#@r+XP3r-m*4m@Kl<*kcB5wU9xD5R7xc-MXNJ#mc>J)5<{d(IwSHE$)IP#hG$qF%l=yWDB`vth`+djw^GPG3Y&&*3~TWqQ7HBG6V1eby;| zOS$Y>F=$KaXHzS>$rv)YitS03qqX*9OS8(Z7xv$eM6j$!hWs&GM`c?2^`&8{?p2nY zxVWiO74lTYjD!adk5mgc-|M9-UHS-3h6)FtFFzrn4b>}Kh!>g&eK(s51T|E};aoVw z(Q*N?!wxE4BLh1l2?@&}1+o=XW+Ww~;CH;ey}i3zM2uDL@^|WEV@xw90w&W-Kms5B zqQJCY$!wp-I3}X5FT$Xz;scWo141Av0^(5bF#IJenVlvrdMhGYKNDbJ z{QN7OIT+Emymw$;%ktEIe!kgvVjM?G8J7C(3h2a&xzdlV@-38&)Vq#X!(^C@YbhD# z2J|HZ|8PY!5vHYW=~dqSU>2Jp?0Ts6w{ypzkfx^IWrqHE6o!x@r6j<->fmd! z@9^Niais#xDs(zFFr zAkYSav<=o^RGCJVNWpsrtV_XjzYO{mW}B7jwd9)ht$~8BOz)fHw0B&~Wr${6A41OS5zLk>WOT+K2+J;J`6#bvrP;eosV%6nO zudcxJZwcja)gm+9adB}M?j)%d#>X1y!LWhB=JSFO`evbL>}{zQxJ|X~@0?%qG%qk& zk+r0$`ei1+L2!aKGH03mQI>Sf?Oj}4TwPrq9czCCPjz5mU~Fz~lKa4kRKM*CgW#y( zV=wJ7YLAju$MSx_+fE|W7m@xP2K7Wh6_l`y1m?mRtPk{sI=;VQ?Q6EEXa?7-bh20W z5JbV;*iGUQN%W^EY&AFy*4zqxyeAwL8b34`s{u`R@Na>!D(KLlpkfQL=i$e`S?CLi ziVmMw#fpBE(3tOrkL!6keOclE{JKhR=WT_Yeo(1C*{;!#fX&(P?ON7k@E+$s$ze3n zgSpvEmVWh0e0$%7L;EhSv)3;qr=fQx-{i>8q&n^$Zm`C;Iy1$XJ9BAAZ8C5$6ySGY z$!I}3D^et(^AL+Q@LXRsrN{ao4y{(RecVo?G>B56Gi+x9$-2`BQd-!9S7izDFH2RA z=P7%hw2mNHE4EX5i=vo*t|L?bhpRrLig7_j!j?{J)?3G%7A3Az>aI5^{-!a=f=U_`K67=T3zVo%u)k7G&2XAV~iy zHw4jXtl|KuoAYyfM@Ly(r(k1ASoPJM3Gkw(okj_1MR6!< zazNUFDgufGBPkQu(+6paxE#C#6nI^ z&d<-!zz`Wl*`)g4gH=KPDe)F1(-H$ZlO{MgIJH6_yQ;0Nt)il0#8(P7Kia8xYgbhEdj@|OtMpP@71NsqNFsTS>-af;YjJcrAhtY<1Qn5u;HL$dpR6@ zjP4t1Ztex+n6@^rOw-S#NkoK%JC9%fcb6#bmpVE+5|+QY$Ad{TzsF^@1F!;tPej?o zLyL&f?J8NxY}P(BHpWPN_Rd~LRW&j474rXG!ayG2c5FX`$kP^4;FAaCKfEMoY#L?Z z&x*VYa!|lXN_uuXTT?LL9?k)z6#{`2gO;2F0&bl^F#x+oAH#wIW??*ary?OCfsrkT zvG*%jdY6JPYvbils&bW{z>^;c`XD00&aX1TaI=ECn6|D0es<$drS5RHfETc0#`diX zI4jgDil4!!?15a=mRm${7xu}8q!ANGk_Lo^N5YS+fCBbO9P`w#TCbmW7 zXlz_Ka{h9!Mka8_w0Fi6yg;u8?ou>|@c2-#`*MPCtLw=(82v`;&)^za{r-MGLY>p^ zEZTcE2i_3G=e)tVfA$1T@Xi370ixfXF&`*!pqG~$Z8EMy!)60PyH9W!iXmZE|Nd^I z)_4Rn!AT2D7cnqud>=ip>wudAyrq{BIhLUwGj-=>ey<8h}QA3 zfsxU^xv43#waug4cyO6-KoWy~zfdj(RiD>FHG_xJ2pp@!fx|usbQ&;3*k?aJJjjBe zic#dKLi+OUZ*%KjDPAH0Pj+_pS;$BYvcdOXpiF>mm-Q0X% ztohvP>g(%^p8>_yq6<_M@Y7VlDF?4li9hDoEBy9;ae?47h5S18yL^GW>i2LFdJWaW zkN5VgEl@s9lItL)@fgLm#`3lsf{57l_H!(Ul{zfTT*%&=jt-!vgI`-w?D!_pskxr6 zSUlE)_(Te$_V4B84BlVxBZfa%2so{!lYYao^d_l@+$A0Y30RgTj+^yTPiI}vdMEc= z5zOXoU?6OJ(Fw*nJ?%gUFEjZYv7X@s1PZ(uAh`Il__UW0Fp4ww1K~W7rV)qw&oVI} z@`k74UYvaVaTidL92=)Jdlr$B2Orr($d*Td9VpCMc;V<`FI@^hXeIi%*6h#+u`rK< zU87k`1m5kn7cj^{j*x);8GH$s5A z+?9+3Bb^@Y?{is9Q|@5lBc_Eyfs4>^!Be4L$%@BF8M}ko4PZfT1MvciN1=A5&28e8 zOgj&Z3wMUUBES8f6QUOM8m;ju(P1)I1j2ce2=cD25U_4|z%(OUfbV)ex9rnX3e87R z#Eq+inHk|sL7yem4>Dv|C(CcI<@6FrRXd`I__Dbjww@mUjo7682LfoJbiM;;fkeo4 zWs25w8>9dghQr8Fz1!hf4ml|qV6Kn$_7q7GX3ewyJvZ1&Yc^{XR_Ll{C=N@z7y=%t zmMaWhn5ohQ3|!!1s3m*}3JQ7#(X{=EoPw@%FQDrDPFHyTjXU~)R9mrp+N|VvYHh4; z*+;PUxJG8H0+)HXxhX^7f$vgVRdoi~a@3q12rl0FJwjYqDFQY#XwhuCZ=Re9+6P&gy%|Q7g;n3x_g775p zY~^m=>E8_!*j<6#3Xt+UcE7)We#@v;|D_LJaT|RZgb>7seI71`be601g|?BG>n&zF zylw%^0Rx~3f=CIZmXXE}oyA5QZ8{+;yBmL5Av{B+v|oGT2${qLGJ zTPga!ANjyES&?*Q>+WJ3$e)AhB01pYl|=zWUjtGJ&~OmP{GLq|Pqgss{w13RR;_F# z9VpL2qGh-5It}qy3&j#Ip9S(wVCrCa8Jqv5F9a+X(Wbf_g0Ek{erXi(st(+JjY>#* z4os*x*+WrFTo*x*4I&)yr&BAhcAEd83xDDg1YBJEFk!`Pejd9u{zoQ>2wWcks4{xB zsfJ?M-?87osed7`F)=X66|Bf)kwF)w7#PNPjBQu1N<_Z^^{Ut6IFMW@+}(A%<^}Ks z+iQpZs}vOg3XTj8D=;+SP3M{30f`SI>TsoTOx9ZgzbF2Eqi21;#{n`VIv8%yE@Yuf zWD-mP@&X}`fQYyWihtf3+XH=JYT6q*;ZVtvT$fU5VpQx&!yYm<<{~nV7=IxUh9|G#bd)hGYe| zo7p`vnNotwDa9Zg_r-o0A` zp^*WqzFjtRr#y#b?$1IqOdDKF#0HUDvKn23S|3J~xfB8AR`rom9D*dvofro~j3n~KB9gGu!eS_>AAws|(JQl!+-RDK*gM65q*jWmgB z`A8YoCh~8M7ZT-U-h*fl!Z4cAf5-=bw~t3m?0bEvwxx(1Z9pN`Lcu5jiux_H-gi-o zI%%{Pe>LtPHv5AS8 zK)2(C58q;0jl`-O^&9>VT_i&hzPKtV$C1ik;lXlU-3$5;vAgSU^* zTx;D`HDHTTpEhnhNjTiHb<`@uLoe>CwY6Bz#2MOz+d*Z5g5ByQ&J>&^&z>S>l&9{oYSfWHop2A1T{#3 zhKl;$VUh=MFv=drXz{u4pPe?k11L8@#*1NT`|L~zI2xSF_#oM65rNCt`2?|gqL@=g zJ$?p(2jy{)N&ulBT1*%|kb@X|0`FoF;^A*DL|Km+i!D)De9t5O8L1U1^e?EXWsu$U zajw^>(mPZL2UqD6*}BI*QG@&~2#H9EmN`wbSZ3nViYCYgP(p?owQz$!SeAekNM^32 zxq`5!>%sKb8#mBS_M|>?qmewUUQNEip;7ja0tjV*$Qybt<1x(!8qZH@m4&e}o1cla zmWRWCJ{C;LwL-lY19RaKcwS2obp^{20!TQ6X)vmZn82MdT>^%=-1Su`VH&qZ{$@@R zW>~tXcB->j-P{i~uT_Z4?*wvy_YZ|+Yxx2B>SxqV){B@Na)=-8ZhUVR&5UE24q2An zIRXInAR8@1a59~kN>SR^(@`YV5d2vNUK({^w~3KKYdtYy_|%Dv$@iZV|C z&VUPBgGP0I;h~};KtX!@v2eknz@spH;Qrhb4kWJE=X3#{l+@12$_^SAx)}XO`;Qw!-Y(;JG_g`lS-)JRrQ&Kwbk9VEt5)#IJm=cxrVqCCA zXJs-34Z$qN(V z)l+T6eSxoBD9f@$trpDi1Yr^cgZ_|_hcDP)>1fQ2uq^-rD5pvrX1e_S;^!z5u1!_W zSnG16-r#V#BLhRL(_CFqLC+CeiYLc0y{I6DqSy;)fO++=uD4&0JwRjqcYU(IyPG5+ z<@%QoGYrU{C*>el=$-^NGsSBxm%J*hjiV#4qu-3WCK!%0#R_wrAB$B>L&J=tAjO1+ zA;#kQiHzE~b5hQ~G~QDy%(klUjRw)8N3~+_M^hq{6X28wicaD(XtDrlA}%sQoJ0gd z*cvG`;XnMuwMaOq$P=-&^*(FR%Rv|6Lq|f(LLJ@9&E8cNW4$Vm5vSKI&1IAU0rv>N zRg!2wg#-d4(36V8!Ukgytp1PCDo~Miea4Ie*Qh-BsW7=`?Vi`=>EZpLmBUUWO@$T8 zC%lgW5fX>m$>s`K1-*iz>zhOypWsX%Inpv~6dSAAGUT+F-T?v%NcO`d#mADUi1Grb zNhlh26)%9QhXWvtZrG}9;s3Hr%i}@ZhIHc^ZyWL^I91Pj0Z>OC6tIy3_RUlk0D6!K zs?uw*99RPGPk()5wM@izc^*Rx$=xkPk`lvEi4o1T-P~2Y8IA&|D8QCl0Tg}gV>A6} z-YFP>G<3H-3S>N_&|$lMgK%3(rHqyx$++Z)(#_erzI>1{4I%>nHBrHG({}|<@KG(E ze+=GOCalb>(aSaiqzA-SF-Pzb$S&JSVtIQCXQJi93{AQgXO1u#5crudFlsO4E9!-e zZhBglqNu@Naf}p@KP%!T6%Hbnh7ww-cmw23Omj3DRh!gocdvOeVHQ9#Zc1X7`$9Z0 zr}>Eg^epR`TA9w)+6pvu9DX9~bdTl(fd=|EAy--WU{m!JKm8vGjjo8K#CpR*-N^rJ zQdEWcu?Jecea5=?Uj-xRUlV|+zYKXVGJaWC^>DhmHSxMwcE0hre^^9Dc!OA91>rE) zgAMmj2oJU1B@oDT1^pgy0rm5lNFwWBeF3UykFQ?6ihA3;%iiX4 zuF8oyi#qVW|U;KSw;i6G9$>T;;cF=_K=lQc9|@{syOqh7S`%JjIQVqP|h zNv+mchT-<&;=_{;_kHxjr)6vBlcgj=kDKGq%F2_Vc1IIww>LL2W~4IKz0NwoKYS_$ z#KP#Z6rn}>>5vM=;_B|gs5aH+I#MjkE}@(q`tWgIE`@0--v>yc^22Ff$5i7>x;6>v58to|zy>2WbEBvC)S284fy0ifD2I-Gtcr{irUJB+UVy;oUi|<& zGp;f@ZSXNR(Or_0PeHaueY4I+qEuVQ>Zj%km}7oYNX`2_85#YuF--y6Z$Hb;Fse<$ znmq=y=>17XuHW!aLVDUX;s17wo)0%SH-iT%$o=owmENJfekNP1x?D1`Fe$VZ?(T?q z%hX`IxH2OH#y`~?GcGgd@t%4@61;Mc^K0FZ{E zbblyk+m`{0BW&N26@srZ{L!dG_9nv{5mBfU02M@ujt&#%QMg0E_4r*%0ol~MK{N?@ z!Aj#_V;Nku^1$aQSTppFx%D!hh$FHwFgblq>b`ng?r982W zS`%5g6~wS?)Ji>usArV?9+Ezj-p6gBUg#9o@b$9>xO$v5yEt+QkgL}QUJ7O5MZ#AA znq;T;E`Xb3jJUq-2B|bqtOx;sFW^t4*Gn_JLT!Z~#866)A&b)sFZ4x8PkjKf{a7;q z=z@6uquF3f8YXfTevS*+M1lCQ#k6RVxz*eY$@un-1OL579u)Agj(2w@Mt~Cp;>=Ln z9=#yeJ+lu2Xc~4=m~Ft^N@MmZW>58`D~2zg{TB;w!_fk{1~JO^#oUoP7L*Rjw25)4 zsVLA?rwe5Oi>9?OckIpXx*=68&4gtI+v|W0;b;q7SFnFtoY+Z_SJnsOK3n<`Y`7_{ z60wqquK0}QnHrW4HtoT+2s&(uU+w}?2}}d^(9#FWw@KfmF`j_(0+1@lz2XLzho9e@ z$LD3@ckdR!MF9Jtb63pSKg8Sg32oO8zFGpDRK&b2U_#_ z-xVw?9a-0I;U9nm{L|W5cmnm|Y*|4Ub!1$j3}9@)LbjaLv*6Uzj`&snsVO?$M3~*k zhf5?%sN&WdBQ_B$Vyp)QONN0=&?=q5Wzs3_Og0vv1uLk*@A(N>Op$~<@i~dS$Z{y>cg>(k zX$+s7P*9K&@k!a(*xp{*UKw5VcH!cWl0{~hJ((vQgTiBLGdB;FHO;#!v@bHS4s?17 z0JC)59>g-)R zo@n>6Vjc#|Zi)9^#75+1!>#7$k*3T&Y|UQzL;KCM_ljn~*0dk_`wuE_=Vmyr*j{q#gm=q!U6q`sR;4zw zb;RkufrbM=aT4Au{fwuIxS%vfQd;lyxgHsT9AmTM3k2<|B3?sl4gl}w>U_%Dd=wPP zC(20Vy!b^=w5?PVyj!` zKEEXLwL3T=f=D>KXqYueROh~-aQ{VR|@4YgxG(k!c3sWk2Nu=r||(o3|F_uiDSZ~tL1EL2s^T1gS}yZeh3aT*FN7QZHbU-6onPR5>@b(=Q6BXa^h0Bi2VFadd8BJ6}akDs1l_;vC?YO$bFoyr%2KuFz|I zI?aQO%5a@VGPtTdMdrx4HDqX4&zekS(A|N1Lf`HN~ zNP`GScXvp4cN|ba=?>{Ubax{mBHi67Al?06_`dIN-23G}?zs1k`{gqHz~OlI*?aA^ zpY=R*&S$Q_@O-;B<|$M>a+2ZAP`YZi;BuuQBe1^I6mv!0gxJgKq{MRuQILRSJ^EA zG8kl5JEibnzkJDzQ|#$8`Hv+HTq>dZ4JRfTBn)qxxwNr?OF&>&;fGUk2Ga6Bj&^o- z&dxbKTcayn*)4Bgjr{jLm^k2YjZL?7*&hINjB76Af5|d{LHO%qIWw$G3AR&VNaJj{gF5R{CV->MRK0*BF?|Wa~m?C!bwO- zjuuKuA9D_8B)WNcyz&te6#VeyUxHRp;q!@;gF`eE2@*2KcX7X~0X0Lz*Myvah$bih zyqlMo2gvdNh;v0w@B?g9xVX4@cve3ZI+-8{kUi!U&$y~L1NJU|5$K0pBOeXp`~SQU zEcmNW`1f@L?O#E|e;0@RF9r7Zb^hc3)5pF~k9Y9ZTmAdx;EMb{{N#FjVPrr1@24NW zDlkmy(|^MOlotPc3BmtO<-z}>kF~{gcX#LIQQuYei(oF!x3zt`2Q;qb0e|2=BhY5N z6nPMDfD36~q@E|jz=&)zrv6o&w7jZpi2zE$SM|kZJ{3LUum1Yth)w_d z7FjhG4h{|pN$QDW$|^hyC(s#8PpeBuJPgaqe<}(EwmgZdbvmGHlE~xX;`-%i3dZw- z>>n|P-&nBwDu-~Mu0P9^z`sX#`|s$dN85v*?4qG?jdgYZ++-T`fA3Sag2Kd9=^OJG zY7?D+I}V`xSHK|_ny2aw^a2-Il!Cpz4^zSJufzrXk{^l9eP+?;P5J=riiiBG@lbe0 z<9?w^R+$d;A1gr*fAr&21Uvws;rO99b-*_TAcTc8ihyba9e0&p=X>+7M8n__5vghu zaH#<>26A|v7SDR1CBUMRdEGA!#KpSVVv&H{u9yc^IpAWiudcXxcnpL_@SoFx|HBKt zxd6g0eeN~@OoC_#mN9vC%P_kayD}gx2CEdX49L`Obe^W`1F$HQR^0(uWV?8F#_~r& z=Q4oYAAD@C)&mI_v&q1KpG8+19``5RR|gyfXmXSw%+<%bs?bQp7OD-EcF*8QT5=@> zbkl<|zdIfh0uN!SZtH@fH^9N{e%;+1%g*{@KFEBYn>(1# ziaDTcYU`4;x5(QC`CffPg9=y&fP(_=qZi?`KL*=D_*Ua;0~!?34vDAkFR z*08n;js(i7=YC^8!NHOMmM}X6rX#@I0@Yv>G*7K!$NY0su|O~)B)kIDm)CO|PRRNS1sye_~1B-=rgQK_}Y0c|i_6K$xrnmak_H^b{zO~k7VXF3e5Ilxg zHtY{Ksr`pUGGFWmENXrK|LzJHxtCQ=fCCN<2)Nj-{hb?O({gDHYPa7toY=YPf5TKK zfx{!qJ|HXBX>wiO-R*Y<%o0ez-NEz$Y&ih;c0dUNJWx0)M#slbLE#3nZb5G}B6fN} zdr0sOCUaqB90BcX1)tlIp*5%ig*HM|6NHz!ijB1v-VtHsE0b|7dtYcY{9vav>%x{b|B$lv(%(Z=uKDpr-}Q zsf%!`VbF#GXxpSLfNKE3Jc4MsUt7hYBcFcJa1x4FU_pJF zU=c_-2|R`j0gB6icabIjrp|E-Q}j&{K+ya`Qr3=u?}ZWuxT&wHDvy29-<5p}M|$lo z&>G*?rU})|l{o53n`|1d_gHw0fB~xEz3T1O$Z-yyoF~n$EmotY=zEB`)Jk;{p);F< z$+T*9_^_Rg)56C4IjQN@nF^f33mlrgjXfCv%l>KdJmlpW&6ZZpT~2yOz#>0gs$E}q zJ;)G<7eGNQW_p%kGGJCL#DciJZZU2UJ6IPWin_I*bXjmYD3Z2-<9R>0+> zJ z8G;}{Ar^d}()&Q#mMFy~QkaKo_$<@tM-UE(^P(kLogmnKe)G-Gw*BY@m;GCtg%?~^ zK+*o;8bE!uV6Z{WW?y_r97vOhSU7YEZipLjUIHdIp;hcHG^%8TAh^l1Q07aFjqL(% zPmI8kl)Hb;IZwBNuSx}&CzRy^${OUMY&D-gu(!ZR(^D#e5}H;iuM9l*U`>diF^qO& zj9xOFt1#-VOEw%nFZ_y-$L7m^O8lnigyB5lN>=?Tx-P($-i$76t*Yt1V!P#H?0Y zfUE&5K$vXEA$D3)QNp|Q8dbjm`R}VB%o|+>XbCdpo+cnxCiejjC`^w#n?Mg*vI4fb$DqK@6pI6AN;tZ@Pr&%943y#8xMik9p;G27xn72t9}|~Q zqr%d^j!kgrKP=!XG|{W|gb2B&vNBkAcqKQC_^>~L$7SKhKM0%0?T^?mmi0@I=dR($ zD0p=8l?yw0>zjkA>{jz(YA+UAIZ|Z5Od{h1J`JC0q(if*n8wF=n6yu}iI)H+cl7eq z;!dKB;cwazz;Dx`vxRvqcduj}QAPd0g)!k`n7OF5frm!F>8|X8C z4!xh7;br`m0@QDgVcWuGSg2i>VD;VH1O->o6aL|aZrZ()m?twEKwsoep2tJpQFfda z{!VcG7>i7)+1)96Hs4P0ZCO1<9*r+$^Yd*0U$xAb+4EE{_VxnkN7K~au{02sGQKlL z4#>*L*+8WFo&XxC<;^Evt5=u|mQbNoh`!YG+^wDjTzXDixS*11v4--l=vNSg;G1iK zwZpSQ1z1oIy@MDGBrys>V{l<$(a!sd@z8EHDjG`YUrqZ+-1)c{1o%7&(l}C=>GvT4*9r?|qWLyt8BB`Hm&&9%!BN?EK1qsC8oNo=GKh4nKEr z1Fel53d!FlO0^FM5q{ zwk(F-o%dAXFV%S)9CmV2H1B~@*3seIwZAmLC9&!%)?~j{@H+LI_cv& zz2!_`5=jKrkZ#cC^{!F-HNsd@qVDBT*U*r{>Hd2rR`Ls7yggYw%GD3$zw)?$Q%7QE zC?fvuC&+uItyd)yw}K#4fW@G&MxGuNs{r!z0)3yDd4PlkQlfg#OBViVaNGGl(vtb5 z0#(B-SR-GNlY?c19XwiK;VjcdBJs_cc#cka&LUE*tyQKc(9XdX^GXzrak5z9_<>h% zaM+Y#2SsHuViH)7gY72wfnU!1oD- zM2wtb3Ii@&_01HJB$Oc(0+sKNtooz1LX}1mxD9p{%m29f8)`G#?LcuOWWY7PO9wkAOcsTnytxgCqkfa zx4t;MMm&epwHYDTJWE#jjEv^Kynb8T#>iOW=f<=&kcBur$Wmi-x4XTrW}XZx&;tO$ zk2qP+)<`o*X6V470hRrZ#rMAj-5*FXGk307B)Me_y?PWoq3+8^K1FQrb^!zUb^_%3 zJiu8a5uqS^00cdYvAj>zGeCx*F+&Do-NR~Y8Z8fC99JMy1=;@oY%O}qF}Pu%Br9ZM z=m+^+M~4AWgg&(2yUk9(!)t;o2i?0@KxnZ#dcHl5A`e-?#GzdWo_7Km7>dEsB?g&51PoTWBwja`a-oB~M<%PY`ReO) zk3+vwE9`e_)H3JzaoerHBbB_9xB&@yVy|Ec`HQG^pb&V?`3rVdxMn_E!>~Np?BN1* zH{An33^SQ-)(cYW0f4RnSxEqQ#YnoDcFmg&kS<-BfqwfZf_R19)<+r$16=S6Ktc{E zdYE+5grU*|GB%FqXHYW&u`CSA#bPVic8WI8_h5m~tg}ansQQvkMhV9>9Ya5DRR&yV{{F)Y-DoACBlUBQyP5^E-bR z5)zlwa(m&z>*_mw^)_G%JYb{?4}GqgY88foSJXS;*)?kc3v-?!P|0vayfJrWrlx-C z?~_>qtakXhya5deNYt6PYjc3KL7j0;Oe8iplGp@_dY}Q5^XkU&r91#^g(JWieb4+~ zx62p8zY;q^>UfeR`{RQdewDx67|2V@Lb>~sxhj#@ zfF<91R8U=Wy?yHlL{4*(Fy{Qp7O!idX^I*K_LtVcb~I4A%Xxbi4d(ma9Z0bF`1p|KT)Qd` zcb_XwZSC$Bcd;4-RUcB|Vj&~20IhvBzLQi(MmGC&kAD|aBVknxu(_?(s8S#Nz!wHI z$*YF9FklD*9&hIY)t1xuV@LAlm!=fq^0|Io02cy~Ig>_J_wexHs4Tzo`ML%lQ^>39 z$}a-fJ@UYvGjvVf4xEv zUT;h9pJr!&@8v3vq4D323BD`?N2jivp-N5oXMe90-?c=WYpz2&L4*Zn1H?#1!J ziD2C}&tNL|K-!(dWi86dF0m$*!;}dQvH8N~5BGuf_Z|wA@!-yk3~#Eenx=_X8@eev za)74M;N9|sQFik@;B7RJIa91t3lv-@u_6ccetAjM1gy-D2BZ^dHC-LF#KHT|h!zJ# zc%+y<6zD10RAZTfH0S31pQlix4T0ze33RAZ=XdlENf zyflo_YZX~OV9_6(UOw(W2x7z-$ZI!%z#NBG>HYU~=qW}1&kzED=Ouuw1+O2gMsR4&k^Nbb--00;Cj_cokI`GRFTGYso9~1!0wF<HxzAHN+FK$dpMAP@fh*dNKFlZX>jw~WTo zLyditr>88}bRa$wevoJ2UD6=Q_VM}W5J;ZX)9>Il_#wr8?(^XK4}rX=C;fW}2*gM2 zJ><#Xzd_1<{(ca=(0}v@|DV5gK7P#n&u;+;@OuCJ_8jGZf53=1<%rTf_J95}#M5CU zX~HJ&|M(gi8F_sE-!JvCk&XosrH{Mge)aRm+bxfNr=+_<&(c?gF67Q!3PQ8qvs9$ zCnN1?wZMWH92{)>5om0Ft7k{3T(dLW%9LX%cgZ9EG)la4R;}wC{;+#G5;rGVik|Nr zOoEN=sg)6`9D046s0`Ko=Y)Drd=d2|tg)AYjIKuqOz1cxF67!6OLZ+Vya zl+&-Y)sjIpk!~8qhZ;y~Yx_iAZ#0l!#nJzC8Xw= zC-ft?y#XKe4+RiqOngyMnC?~7>o0{|S`zHLN2NrE`RFOabq_AB3eaJTFcg4+nyR`8 zK_ERAD}$AHI<*ZAn(}7)2CFK?^8pW&R9Q&tF*BaKFgI(xULReb$B#AhHUk0<48DcuFMg%5Aco7 zi5`Vj@-;Tx`2jTqT%#-rw+PNZuJ4yu5 zcQ3c_AnAl=78W1z(I5~d0z0JdP4`E|VkVT6NPpJWlss#wB9W@<)GPd`Iy0rIPl7CE z_A9os{OJtYfNk;tJXzWM>_?U-r%{4)(E90mKh-59>AfF1acZm<{5U}`RL$sum%ap} zxJ2RU_UC=^+*`;j-K8QvnQ~%^VVEq0&GgE1u(O$5r#z38CTWtDOSVnH%|nVee!IPq zuBh?v<3k&XkD$k%f>WwyLW#!|jL2JZt=ehA6u@xZv%Hd2$zDdpbm(Wiuwt&qH$I2V zn{fx+v**dj?U$Q6yJGCH|M-;+FHwY%FbyYua#DwoEX6M6ij31 z#a&&6_HU3-LVbh$y-DHWuFwnCUDpcI8Z8vD=yT=rHm3Kh#~B+`_SU=XZOHOwyxShM zh82(&7n2huC1RySH6L4xNf@WbWPk9~82EIE)|H4{)t(%^uk!@T!pO~hcAfGPnz41jTY=l1SKhdiEBFL2l|M$oTv7K~~V5#dqWOZ4lor5)n z_4xIvk&Va_>YDSF+rd8#`Z@@smcj};Z%!g099X|6v}@pU=s z_JkJwP)ded&kGo+UTw)Kp7s^y&9xI`P*Auv6xc_KPh(;>DZ>@fj{OrJeSiz_qi?DT zRLm|JrQJrgIQ^k>mLu!?NjS?2Vume+m|g;tj+Zt@odZ&V@#1*s3mD?+)td`x3vDM> zrble&YP7?u@w0qA&}oFli>f&?X;k+4XA*-O{2Eh5;7)y-)iU$Pt+q%`nb5nQkEl^e z9Dj%6$c2D_5M5R_)Y+L$$oBT+^wi4A>h$EKpi%qpILJLx#`8(4HoZj9Y{Wg4O+~`ZWp~d zt>pA@Sj(Ce%6V3?b%Y&Hl;Rz*mbXg%V!auEpH9MUwZ$Q_Pt=a6LRZpEO|rR9%TzCC zZ&JDk2l<_ETpg7ht$JrtxSeC7qYc&ptnWy|sM~NeD*Ng!8(XjLM-}IqbDp#dzYmHA z2CRw($m5GS%T3*RrdMCB(sj*WI_c{x*h@$hCyHnh2?}=nhMYPEQ?3tmcZURqjt@`B z0J>^$b7%Yaho7Ai;^Jmz&=IqjM0kS>w}}%Q8yg4+h(wbGG&D4)WaNUnvhh6QkOMXr z78WfM-izlXj8sfE23EVje_u$u-~moKDk>8+jAvvVO1r>A76bc|Jr~ip<|M6@)#3X0 z51Cx$lyWrXBneB}EKrq&?WEqX$E5x^5AVyfO3$OFQl`N%-rTtM%T?9)*Dc+K z-o!4%U;n5xOB3F3GbRXEy_yg2(dBO}KZrM4ve##;Y>sKBBt|RNN|m3czmLcFk!{EUZ5+REnZ6+an@+C@aT;LP)z6YA{unv0mKI zf3UyXXz$=)WN7$XT|%KCMR5n(?BNOR4dQ`bX@Oqwpejwf|dQ6n)(27WtS_BYrb=N1}C z5#Q>d$7h$V{Sk3-I@Q*rrK7nT8sEI70V6UuKW{RaAk>On7d3-h&0%w=siSj8vAx>Le2javr`NA0F*3@s%`?iW?F?#GRct&3_ro3)bLu>lCL}<6Ctb%xp2(O}q zgU|cz-KvwV^t^twkLu$5++L%lzfO9*k8Z{q<{ob{xk#m|s+tO4YVZ~9$07?G8yg1) z*xDo@ICykqgjUvS)_H$iPBrQD7#kJ!^ycOUD|c|d$zd?3Bvv`a93R!%z)JYhMROWI z%6DrZI{*wL@f}%vx7>l~&b)_i&~M7F>MzfJOBOQ}r&cT$QxjV;^sKRk(jJ#m`b=ru zTw_YKV!Hzk>$iw+wxG)A_ihD_&Vc#MYDjtl;g4zUQggP*mVSvOWWahp_w@cYfPcNO zJSBv-@D62Ae}v^y7nf~w_Z-%*dZ*-JOXMYvq$4J^5o5K0RqHx`la=A$qVH5)K)BmN z!pJ}&F9IoXq-+SH+T>B2R4A$7gT{S(qE}K`+j)e!N{#}xq+2$W?6pML)6jP|d!yP- zVWk46inHIUo|FG&WgHnx=TmT9k>C2@3R`7Lg#pbQ90wFF-}FVqlswy-W}skWYkeO< zvbE{De$_83n`&ZYGRocY1XWHR<0kPb%KiKGg?BBqJv~l4V48tgyC0T6o^JxSq8M5R`!;EY#uXMB%N-IrYyliDLO(4HZ` zWo9-VZgK-k%<-|HmKm@YZ_BcoiMP05hbDW9vLIl~Wrf2HzU*7>XJ+OgYfK`{k)9U{%p zP^=_r$9a4!4Czi=!ypX4JhWI)^#lDDynlhfKE$Rmw)tm*&fc zx>@@@9G=j&g`}7E_Q#uO%!R*!JyaUyv>nCC!TeJ$f?pX{s0;8pCuTWxb8RHiG0kfM z_g9B1Dk2gTH3gad7w-CmKI&9fhc{h4lC#;JO{yzb6u2sRUyzICnvCNzC8dri*bT%p zMz)z&wH2*?e1@C%lhlEyWK+NU<}p2>v2&J8X;gdPNI^x#R4TWvCLl~9z&y~`N2{xB zPq@8pGsk=O@@{gtttAZR-_&%|S)Mi3NHv(;x^px%(NI#KK;u6R^S-Ix2!{cmm!z7# zn24A^6ZvomtA!laAZ2$l-%pNC!Xn0M-2DF0h=O9Xrr3I}!DG3nx(k)q$qA@cJP!7n z7rR@|K4%hA3412o%Alg_xSah>%@L1%-i!8uBdohPOQcDlD06} zNx;MqnKs+?&&J;RSPlin8r`Igkzr~Imz9D))DyBI1*~yYR8(wfI-A_Ixm~O$iohNc zaH3&1fz8+3r@xs@^O(u<| zZwnO#20)m`25pD0aeJQ;U#erZSQ8qn)#Mn-0L*t}(%!5d!6X;0hu9#VU)?#z)jcRV zc^T>0qWSexM7 z$Au?36UF&sbu#`QK72wW5;tZeWJBSNUVPGQ6lUgR#nayfm0dePa@|;VvUbumqCp|Q z&4IrdG}ZsOpQiZy=>#zG(4!=Po4n=dVm?2o9%cHt{EDw>aR+8aIlNm&Qci} z!R#m4y|z2Ee#-KB9M*_qw*7|%1d*I5$g0M(e9Qt4#Ump$x9nDPwkJ>9);p^i`X;Pq zwzp2h+LSt6Z#EO_oQNhM1QjQhzLq_?j1mg@3-Lpu+2#Z?HeI$L68&Z#l=3 z+L2CBUwck{OTufPJhFc=%s=i@#sm#fqzYqWWy|niTZ_?j(WZu^z4MlrR*n^9!r`tq zV{y*tqw}z&3DqWeBvSajKhB<6QOTrUY~^AsEbN04 z1&={%2*3+Km5A^J!QIVG0b+~;l6%w_oMoDmC4ltGiRb2SxIW+Yy81H>2oa!&j!)Ja zt1T)hsPUK_0AQnf>zzb77qDY_jI{w5fCVSr=Asj0@1lhi8x_U*X7E!KCPi&oT~R-t zkjRaT+rg)V8ipb^=&r?xX_sR7a{(Y$5h*MtvHC^#M~b9ud)MMV2JMs$ec>`9?$^r+ zmHUY#S7cWrDz#JEkQC$!1x+UZ5tVHT3M#^xSA0ZqTuiETM4`8pe_m6#w!S>vJVvp; z8x~X5j_dI#b*(8e3yFOLrv@dOFSROa6blw~+EtOsf~JtiNKq}TFhuZx5cT09|c>8PTb zE%7UNk`NL&?-Ohz<)M28Q@?Mzf1buxk+R&g`Uxb>Ky9^iRVhGm_cp(}c=xUIJvsiL z(H<3pF2=Nq9hwE&SH%T)z#P=WXL)v5DGmopL)NAkb*r=CSx5~B=Y@_g3%2c|qZ?ji zn*My5+)R_5kPzzv32aH-vtiAF<-nSc54~15_O2$p20wLxxJ|hI+0J+PP5IZb)Fm7l zKIuUWd3m(6no-IykBc=LieEtRi;fZbXIm>rxRNpknqM?PUe^Pl%(}U`&0AZ}`nvDsCHsOO_r(h-Ccp*((}-Wj zzcM$6hRRThCUjfu*H02ycP2_fU7dT>#QRPaZ0>hF%Z1Y7dwhBf+bq#;pr)l&2vknt z=54F}Bs}R-Z8oY=`y9a-`TNOiMG*Q6NP((x&C>ao`qWGpwT7&E+DVL=F$t^lY9%PN z&6_Eho6N%e*U#Lwk@oBowtbJ+^js@1@na8rm|(8`HgE|3E^{ksO_G`ZOQvlR-PI|RJH9@r$=HbvEguz0_9K$ z$}wq0-)evKEwiO2@y`ZI8x(^e=FE&(gNBTagNLIv)Jh%~b_#c1%~&2WfR0mB!;ly& zAurQ2@_ty>^llfk`o;#gFmdKHy5C`by)G7;vI5C6ldh;XBGFyDsF%%;x6JY zlGPPTjT)C*5HVD?S*qpXyqld_G*_4LiVrh7YJ)*XhM?4nPJqfp0Q%^MaE-mMJDPg~ zHvvI{11=*hegf19m6bRNHX*0Hxw$AC6UKPYg7vm)pUr3_#&~#ndI<@3>{`Khd>tDf zr!H!6bG^DmoG3O>Ur~`JWOu#WK0VcRX>>McI>H_AVmQA1{E3mkf3*YP7z}#5TMLBR^0w90)v>XWp&>!oC8OO# z5rxTA7ipR&jq^hX(Zdwv=fZ`ZZby)%`}W=1!=NLD!~zzYEw?MScV{gdhy0hXJG2|= z4i}$KNpJdc=ssf`S2R`9>>n0%>ir=XnHW8Cq{e5K@EL(YIe`(iI%Z9B*EHXX^ulME zAWEfn8hHoRLxV6(SF3EPyAlqgFKOewGM4U}-x`0?aip=7fL zrjp>z-GNJSwrnv!9t)$nlm2a3byc;Lbldq>;16ECMqQ_%IT7EE+0&H1o5x%Fc>Id4 zxX@oI2@(VbS}U@w43Yan}OpDLe=KQL1>+WosO4}2($)r7AB)_^GoqOYX_j#H%)B-6- z+;w$NzDYwPRMpiNroxbTi2PN2pU>6tEF^!O_kB`>ip@g9sLr3=AmjWDe=1pBxxixS z`IVhnjY(owdo@A*75`qZ{TFmn^KTpmLjL@Z@XuGR^q%r6DF8n_?HqK(xZC zM5`5+mBpaw+I4dS_m+qw8zJo+O3s4}irlSQ)mlbvfY)<-?dfxcKZW2+H1(<$r6c4U z^@4_2;nseQYHQ&~-y{qT4FR0CrK%1r*C}NaV-gd4HaN3&jbfl?9~T#zf%25_t!sc- z3U)TYgN5v<;!B8 zPtdhuOZdS0)s^MG&wB3&`OXyNZlJuQ;X9F_Z$X2}xo=SVA3c@fJLc3-Z63oIzcBf;O6e)kPhylmGTf7UYM0`oY7^BMTuG@QTK=&86MwrVyL-7;dh!`)B19vpnp<04QdCM7yks| zkz7hMAMGW`DzxG@RDB<0I=GG)E{;sM5s?TI^pM9pZ)6@`hQ{V~rsc>l+n(p{M_sYw zz3t;UA!1L*B!hHtkTJcYVS06adD`LuI;Rzt6x@N$%FZMZV|$GO9hmyX*8|`a4!ji3 zw??lk38v=8J_rabe~&cyY{VKJ64K|vh(0vo>s=NDD<2lr2pMMSM-H-{iMw1AF4>SYFF2mAcx=D@l-pBiIPm*3Y&ZVrHcX6z(I1%;bi zxZjgu#l==;AQ$_Nz8|ToAY=UqR*{|;qCMHJjGcW)$Z(-GTn6J*>pjLh;3(`n8JPnlV@5j4_Kb+0HLqIp@b2<_c2 z5h9VMqcAah!;y?N#QWEJ#UyJghU;!vCKi-nsR_u$s>U)cunhadc2Jv07mqQv1`y9_ zEEeR9MebX_4XDdGAh`v+{zG(YZC;P#7!me|Qp zE;-F<^O%d#n8-L&1<~D`O8gy-p13bTmd`KF(I4S2vC;$GnAJxN>=!SHi5Vzp@(T)# z4Gr^(ijw2wUG{D+ha!epO$N_)W^4aEqo<giS(JCu4P*183B}%(7qxh^*RsEKy#-YJ&bAgD% zW!WWv{gVJ0N<8vLdWn@ayP=-02L4d4s*>X~n7jK>FNm?gvMmiaM+(2v=_d*DNY&g> z`MY)7l4(p{Bqz4F+ z#GA3G#z(j#XMw&D#;NW~Uqjg=`9&k9QrRPCHcV~vog0wcl}S49PI0$q`Tz185CI7x z4?VV&uf%1X%#ZTxNQUi#QUXnr--NX|KOg7KY3Y6(WMYrPY{EpD46PazM?kV)5W zeEKG1ND`&;IzeWJwyyFG!V$;|N}7Y{?j+-J%2Ib*`klh-KMc(7OIk6mHr;ycM@#Cl zhmr^#QS{#_Iu{mvy8!8)9Q5STK;KvlNxOfHE6q9RP-1-j zdJXi5fld)vc!-gbzP?uQk*1WTmY{xSODc&G?;ZnkgM|uXU_eOEuU~K8zKx(O5|xlJ zKWtjmUtIxd*R-BNPj3Ya%L!R*(pHchUoYNZE;kI8t*ji(D27{a5OHwTuB5E=VIfmH zIL21FKlPGTb*j__o&8hgf!0ukeTBuH8X*BraZz@scAV6(`(Bam%1ZtxagZ`<)mJO0 zNL$=Ly?!oWmzekldh#?f2aRB=)TDMm{1?rWD|g4c*=noUloZ9QX#|Koz-D(QN~EQw zK}*!q@g=rmz2@nNO)S`ITsDD)>-AeDO-aek))?NzP4^!bz~(xX4rq7-1B2xy z_v;CU<;1OGUGEgOkL|8kySc^1#f62vJ-{}GqA9Uk1#+-z$E=JrH`!3-Vw6qce6KHb z$@-SPS4#HNj{S?p=>U>Bb4wY$k;Vk;S`qGd%Vtg?F@`V-?;Q)wgoxst*YlyoYA`@ref9_y{6KhU*)egtRo>5*7A}d25bCqOAvf9&w^^I?dol(cSYjSRo@71BKn@ zpa?eMmX=u|PRh#}k3&H?>H6zJB1B=M*Raj;;!8U`V&dwBeQO-dB(t1cYESl?JF_1Q zlN_q+VJG2{)c_2Qjf|t;b(DX{v}>XP@ z>_Q`zjV;EAP_w{G0MOLi4vl3cr*@2tLjB=*NJKG?Svf2IF*d>*6Dx@(@(ONdeo5N$ zKfDtWt-W2BlbYOPU||h^(x@K8vS0hz`M`!LprvViK4|c|$N8@H z#Rgv1vpHbQyKODZwlm#~!dK9U^KcY7r<1p91N24lKk@oLQ&Jr_i|UHdKQ6O{WEs`S z8AVzGWvXQc*c+Xk2UgoN8p2|pM>ysKWZ^*Mc^u5KiIZxpaj_!ta3shQ&ua#Xwt3U- z>+@AaJ!I_|qG?tmW@P)KCfew1v27ww+a+Ko)0*>zRz-a?2nrvofd2P$-|;RE;h5t+ zkWlP@biCfl%sv8wx{4B+8q&Gdo>QF)`u<`O3i9TxcK@m-;O?3#x5?n4TE`SKo8QfH z&Zr7!5p{s9#rJ~)yX2E|zmpeAX_pm+{gb*0-2FdXb|?Gm6|}g8Op}t0l})U_ z-V-DlW6lHJUHzvL5x{at!=>`i>U)Jcy@#Qe3CErn`W z&V}JTexPpebIC%K~UfC+^-_?k1)-L_88n7#sZ|D3?-#enxXAv zJ89X16S*3m-`>Q-aaq2vIACjTkFQsGJ0JLZgr#gxaoKupg(#?~18e)qrVer`Q}R?j z+A>Qb?M>7U7oM4NV5M_;+Ea@KlkgMG=%|>Wz`${ut*$08u@Hd7=(nn!Q|)62r1MAk zOCJu)Dbi$UePjo@P`o-6OU7cZOouVI{KHRRW)Rb^53dfY zv^>{6{@&k_z6dWK!PPp%F7V+@U@m)^S_MI;R}}|+4QTF zoWWH=JUF}=!D;>+MQ1WgXS>O9cjaHw21~nuP(*3$WN|~;1&bCHrhBEVHD3(whTY*R z;!;@|^KyJU+&AGKf2sgnz;X7_cOp~uVOn6-uc&4}YL}*Mk<;%8Dpm1hqUaJ^c2Xn9 zQGc%fHMp95o!sPoT?)BZ6GcrB9X1!vV#jMWFAry!=-+Dx$1i9Hm*1NeLKE`?e#fxn zcYng)DFC?(CxxJVOGpU3w3LFKlEsq&q!Wa;#XP=$-j79lr1oA_wYDMY;9j&=?!UvO zH~oJiJ^X)N%lQBI1IncU*#;2gjE#@I24c%v%!j+|^OEE-^Lyn+hx3Jzbvk|UW3a0B zYkrnvhigjJi{kG@&_{TL-zjfyzMWKSP0_NhXOo+gL#d@8C-;63FmmnwOgb%nJw3S7 z6Yq5w+uLs{JwQfpxj1my{zFpB*{CruLL&dKEbDy{Da4l(`RTLm`C9Kl8|_y2e7h`> zx!$FS=RdzlKYR9!ccBG$;JZPWbVxwJ^XVF!v;`06{hL1vJ7eWq6|Tcfqd8oh>{61F z*O{;KlK+YNoyd=w177iHCYgl4w4QHS?H=}453H$~*dv~0pr=1MarB>TvR|j=Q=ITR z-YCv8@G6;GU$ElmZyon8_BuGgnDsnO^X9C!(!66gHO&J)UBCB$Uc!MiRsjh}YjFak z2ZmLmi}zF@K6OHnCw5@qjLg(Mj&o3fd&nf9^)V?ZV4$@MPxtsF!NGF*XuPofky@n) zYColP9FykAL_J({llx_6eS*4h5*ZWKxj&-ZbSxNEPaZ&Cznb*u{w1cPJDlDy`xvr< z28MYig3MP90(QeWhm3u0RU z(kq5bUZ{YW)D>*AoG%fGEgc~5i^w3pY8K;}6ty=?Z9>r)Y4A(H%F3=6>_lKQKesdK z@LXs4*J$p^T(c&)3MnuT?pciuNU)ibTXu-~>f%3LOW=DTKn|oIj-#lmYB7?12s{@| zFE@&3`9tv;Z}z~a{dIL(?1f)L78|_2rq0H_b>?Pb0ucE=kgNL&37P|I!^xhWi7pKh zk({cyp{o+05{K?4rbfySwzUnHLs3yfgGgkif!Gtflj?PLcJ^y3ictr14UJ71Vt`5B z-oowiyQUXrje=c<)ThH4){ZZsP++*VaCy!LJ#>F8y{}Hx-|LZ%>*-rcG2ELBE|B>m@=VPJDhY0m zj-Q5p&CLayFQzDa@Mz-T#4_=`hd?e`qhnI3XxbE&v2Wp%bV)DhNNLDelnRvqN_mGt zK-j|q@?P53SjYkK!^D^@7xKXMDb;CAO-W%mU!DGMV4dT(X#K`CkNgH`8U9Q+7b^GS;_y9;QI6Wa zbuC5dqCYH6M{aO9^I&HnukQ>+htRZ#Ph)lsWjjQw+xl?U0SAGm){4Qe_m-T6o55We z<<%?^J%PCZO0#OyA@Z$jF#Jp>aJ*TTwr=QR;$AbcDX@pyd67T@u;BHcyI9Rush1zg z*4+HL3}!FZEMM>SKjuH*8jq$TYkO?_;e%gNBcc&Y&4c;vR=vk0BRS9r^Zp+7CS{Ow z)j9o%3?!1sFycl>2e0EABa7VH5~g66-7sSQa`EV{-gw5yxVQ$l>w(*A#-a#?-%R|n z44a=kYcPd{e}F>IX0bdv^WAK=%i^8<@G$Pi#@(YwCr2wVx+D%Oz#e*D{kcph>!3a% z#K(UzSNql8)ZBdLlhD_=(hGy}ZMItL#d^=>rlO+00)EvI^ zv|9xTVo4a$NopLNS8x^#E-#}+0ek5IqLTl1aS{hG^dWv{9|K!-$d#=zCA)AGq-Y&T zl|74ROUEdn)~bl# zmcYM*g9tzO5ty5vownyocG;g_eSEORYLXAqcw^oS#-U!-65B@?d$-pob1m9D%QIls z<=nCO;!+1&&y2z7*M5%h?u#hu~%P?XI^<7-d6JTzvTGe{uGfQB_54 z+b9i!fYO};0sFAl)h5CEeZKd8W_vz3&<0{5t1282&)q?6vlq zbKduLUy&Px{-Sw3+k}j7;=F%>35>5kRZcZDjHw88RybGZUmtSd%+TQFrA8&)KMnj@ ziyXJ)fobh7E-tQ-ad5bq&hXUjbKTiG=d}Oi?62Qr{B&V!V6X*@TIADL+ia;?nah>U zMh^G{{|Km6lr|;1i~w4vt4t~l-dB%}Eov+;iVq)5XX`Sn`5~QdM~~CE-njTWp3X~- z{Ru?gHd-g*G0iPVxP5_!pPJ%>TLXFDRYce5eXjty_rlR;B;OonzP5G)I4x3bLs^L^nF6x0BtxtjEG?|tvFk?LdIlSd{H`7ne7;l z?sZYecw#B51K#wGS;aXe*q_~Fz-;@fg55ss4S~coQP{SEB zCr`*~mx#V!HGg#%W!&EVy3PEL<_o_GQ`FkaLf@>^b##M6LEP&$5&`I zSDFu;)~uZpuWfJhcr4)nf-gAw15t>x{5Pp&=u-J2d^EMSiSl^y@y*D|1=kl)2)HqT zBN|dHs{c@0+Cr5-x#%k$krzm+?)0Ip{{EsFya5iI$&2E@W)9|y3hB_)#uPxPq8YIV zgae};R@sj0`Ich zbpSusWF$6}+w`sMS&^WR9xLG&{{yPS6qkV9u;r}*m9~96SyXj|2y}CqqPvW)Q>1S^ zpuizWvRG{?f4I`(jm-mtI@=4x^7i1IzUnY(dx@sbFMRFaw<{s+4U<}#%;(k>AM(&& z7WlRwg5gR#A@mR+alB?n=aXQ+OGqCXi6xY4W+k+`+!)Ya?X85ten+f7AxcNNaR8q+ zpjTcWO=#uPAiA1&j?Snwq#pL<)x;+yiIbKcQ6dww#u};yhv>LzW|RpjDxSUfyLj3X zN2j`^tm*<~O_jf;mauVszMuH~^|NcI&Vy=&)_%>nkK!Whkrt;bq__7`xLg7vd?Y>& z&hHB7a?NI5yE78)^|-$^_Zz~(I*?+LlGVRmZckd}^?RRh4y9bVn!Av%S6e(f4VNVo z7^-36U0tIn|JGIohq((OWo7Apg7Cb7fP~1Td7d66`Ap+Gxo-vtxc3_E5w;Wxqb+xu zpOZE~ITsR`h@G(zESMBSbWx>1woMxjYp}bc_>JcTZ2&W%3Y^V<9{i{~+iqlE9K)e2 zFu>-ngpnJ-i9393^{^Y%!Nb-?P4@Hjz@&fWrFvqqV;W3L@1LMv``;EvexVbd2du#f z#2hx44|&^h?r7=lviVwolFNHl@?3o@9&40?W?Q(e!SJTdhIKratNQl~ZTnV+BSz07 zpsUAwyb`E2>maobEVi69QJ6R4=#^K9?I#S@OBF9pOSm66SQS%!;H>V3_(VZ%<5c(T z#kU&1u;%~u&#boIhCdiWT z!H0F<9#lr`xIINWIT+0lh4Dw%)SwX||5j6sjL+|UWM{ljE&0&kHs-4Yj!#qGm4D#~(VH5R008ovLdFzb`;6Tg9_He`kM_(c%B z{3aKEoVN6vB_D+`-k_7eN__`YNvVK+qcQ(uS}sbBdG6_K88w9=fcH`yC3w(4Fg!$q zy??1w>G~oBr@)y(Ko4e}BSEL5b+N3nE<07}vgrVz+z`eFmul^nwzT%}ik%h+g^=)8 zqOr@<|9C{hRa`G5#kYv6tH0i=^nP3C~02lyF^T+aT2rSqIM14W_} zQEz-y=~N`JlVI}$E+?n1;swh$KjI|=>eMa zWw3XZK*o4hQ(H@TbgXvPZ2u@z4Sb(ArIVPW@Z~>I(Joet)_X>zWo}OykI^Uc-7Z|? z$3_Y_Img8CdFUP;P1n_=5K&Xpc+`n=auQcW1cwGcRY5$##t8|zJ;7-S=J=TfGf#(< z;~^p;?IOBfA9X^V^$e+ggm`>}A0K!!yUviYSp3a^0&I%0(C{RZZk~E1F+%A+4L4dC zzp@TZ4TYpgFYu9kpE#A=uiUy72C43PQ=*S8o?|N(t1FH9@&l?uDT!`7TmTy@lmKTp zuXDoHAlp2jLgekolb40rt$ed3=&|nSdS0mm_3BKo+f$xS;xzeVWYnb>>nrW<1{sm< zmZ0~W8&M>46GL;&mgl$}_cd-?+6E#HFFHANDKCy{N7E<07+xl%?D^9WE&FbtH3}@H z^T1JE8WNXtz6zs08SMj(o>=dFHSO|3bY*ev8l9%h&%BCl+Y;#yACNW3?-H{LNEhej zLz>e$xKzi@%Z*b?Xn11Au)f&BAkhR*UdctqWnbFvu{|1!K2yJ)s!Pb|o7@CjX?#H5 z%5YIY@bI}a%JYj8er$^PzG^!hSbSVy@p;pyaaBUNT`$PYU0U7t(bf@7(mW#wn%uSz z!nK;`wGt8yhg9=(bAKP^#l;4{lt$&o z)cq~k+QE#L7`k0T@7kyewa36hW-Nfg$4lAXNE)9h?=?2t0drW>VyrwxEH7vO*^L*E z?$?WuME)=>9Sydrc$stZrIS@O3c;v|dLep(wq+Rm+MK}XHTLcPBm`DqjD zEj#SkKfHr)GLYc@17ZUL66b5C#o|oKY^mW8KI@Cc_wQ?acLQON8ug|FbP*ZsN$Ff3 zoG(eL?8;x*6ga=o9GF2_n-BN3=j$xh-^0f3?eE(JM!w4+;YLXc3jK6I29gGRXrBx) zu8z6-WK)lpipUQ=fijjDsY5jUMUrJopx%<$iXbj2DJeNw!pz}xCXOgeR~JMatyEdy zH}{?PN~ajhy!zK=yH_?=k&!cw0{QZ*U)IGFOKW8gXP+>`0Hrh29jqyy|=qQ;c^L-ht(r-z{~q=>MZ z?V&yLvb?g$*m##~LjqV;hk`t}tkJ~{xSB}~PgZuo3_F+VysdM~I-j&Rqgl0=WWWDB zNcSjU-5Co`UeDxy0A^z8<6Z{RDOBy%ZBJ=Sx-U=44h=iI0kH2v zxw+|5RMu+t&|mTsx6*F^F5av%l020!L&Rk?9jQxB#El_kv3+>ByZ5bK<~Z9ujmuNF ztD&-I5e(?TKu=>78uTMH9l}#+!juNhPKa=y0pZ{)s*2Mpx%XhZN9f7g%mV;2sE9-w zojzP3iV~h?=+b^^P`}&0((YBPb5?wpCkl=TRqbApZevBs;0kIdH5|%_(aP6r;*7#> z0)0YfLAraY6>)`VpX**b=9V*VN^aIecPAG`T9)8dAE!^GjKc$73+O4a)yF>zs|As< z$AoAKn8tG|N4xlq@EQCJ49*Xeiar=`!3rBnNWLjv;23e4RIwWDnALvMhGD5AYb#=< zFU(PN4?XhIh9lsb3m>OwaTG5JAt!^-0Rc5RIeEOuWs6R7do+=A#|em(hLDMN$CGL* zb$((TE?csnAgop!%?@J4!HBKvw29kku3*)RJP>7khdb+N5Y}pR!ZV^0mbE|$mnIKQ z*WL9NRQkj7w&b38q)#)K*bMWzim`=_xLr7w7|a!CbpwQ4HM`m<#H@CpTQzt7`r6i| zF@TZ&hfbruLZdpFR!L4SkzJP?sUzAyK^GNg{IsIhCxvFJ(fxU$mfu(Q3G%4Muv}P-Qz+UbCB`L!#>e*6t;@j_z z!nYFrNN`Dt>iNEjH9aAS^M2m|XodP>N|2H&=sK@i~NFq3zoL(i8&b&qRt{9M;B4Q zSlC;DEhK^Y?nQRISgQ{?*WEqW+2%K@u?~nIwgqUk`-=s)P1pF(&`>aJ;s?v8Tjx1T zU-#-QPsl~WftrC@RhFpzM{o$;VqNWzmC!?5kMj$P6pUiSkR3rdI&$JaB_{q# zpP!%i7&qZ@8k|k&_febjx;$<5j%X}N%{DeR9zw{SQuxAyP^2g_!M(^v|(F8Ou&@BHFsO34sQuD&tGA%l8hpv z1+88iZMa=|Oln)}8z(;Oa>vGIq)~8Z50yoH?8m`Hh%;C)YYDEg(7MXI$b^LcAdwsp z{Y4vi8K{X_F6M7pWdfk|_Nm%-?<@*HyOt*>_NB+_^`<74mMxDdZmLr~1lSW=F>;O6=tQT;w7PIG{?FEwqSqG^zo3dG6% zZn1x4ZSIO!bz;0soFG3wKK6&^6W*gsVMQVM!VI>l{@?F4Sgo>|&@$ zij}3} zb-cihg9Ga6Y8sE@%Dk=5KqPfFh|R-DPhe0ZTOv)0qHkd8UT9az;E|>O3H^IuPo7+r zUPmJ8v3jIjjzYgTY`2Rv>ld&4P6gPfc zuxqo>?8lE(UT3%G0O>~*f=t28_-eIg!o%4d76A4ajzdC0d}ZNOVt@W?Z44cC@qQG} z_V$se4gWk1{`;5TaYQ_GN@~%VjrK7J)Z#&4zuk-KWB8u%0@YC;0P*&0VjcQ1COX=@ zv`kXNn{58NB%wjAR6>v%#K{7Ag*g%;B51I{lz@$miH=uN5qMCFyd`Eey6Oi@2M`b+ z0JPC zn|tnFgmb0#p@;h;I~P|`@vrb%Qy{t$`u00DmB0cG6Z3ksFAy;-EX?e0>A>~#&q9Nf z)YUzHGZz=F1mn!=T$F~guVnoQdoJt>!8rqizdJXY~7w+6|Qy4lps1d|~l zHg*fn?+}w_Onb1M^52-u>+}CxQ5buqaQv@D`8-6Z5Qk5{Cmwap80CW)fM;0 zAG$}0jGMjTMJGF7i;9^^&bmPiAB1Wx&2yiY+}CFGMbD-Gyoc3x8e{}+n=u504&`#1 zR~yymlmC1_Q=s?-y&1((_@ZF>o_7^WkqRAMlxrKEl4eke!o*j)vBl z08GCrDJdr>3T*+F0U)C zlQZ+Ru8zPSo8WL36$$BZy796H3H#u$?Z-BC;MLy>!rM61s}&Yl?ZPw^)V0qq)ZC@= zuL~&@oc5iMCUaiAAJqXRv-VOwarXu*ZW37AFxG&OTmXRwVaTMg+RqFp>EQZmX=-B8 zt8X1|D^DOBAKe7~8oODV5Rg#tiQC%RcGS99nEYDl$h53X1-*a!I?H_YD*rb%CS*)c zqSSPqv^_SoC8V7zzCqdi=P2MfZ*u)*9>i0t{4N>}M)4%)&xL%Qf2!bamxfELt{%W|m zJ?>c^xT>)%Ca89%^Oawv*c1w_Y4rl_kd>6AEWq&T~W6NkRVE&}8bB5H@2FwdeCx@ZSSCdv z=mk|&X1Iun)jxk>o}QU0KiTnWxbuLv*4ADDrNwNi##kH<4$hP7R;GK8C^93c(OvEj z>{oePQ`pSZuTwl{0NmGPPsP)S#+$KPYLcT^FouVpV(mYc!K2msFlM{U;2y&ID+`1x zfLJ`M#U}fcmPX()zXo*%QjgP8Q&*Z=k>4UHDX4&Zw)#BA*VxDb%&^p}9gcww_sGaF zuiM>kgZ)V=+4ON0;8Fi<(v7g5JT)~1hM+M!kefv@fhsn`niG_{9Mhhh>&( zMLLF;ppUQG0C04xvsgXZ-JNZa-_*%})zhk{$IQ>qKLL8|(KN34`FW5?4Qf(>GIgH` zPT-p5`O-MJ{3l172VUg@_JMgS>$l^M4KwzQ3{P8~O2@UPn8bkS@}6uq zTT(&-#HVPBKF0WtU~eMLw1WgI1{zvgzz{s&uPy)<;3<4oL?J~LT3QS`jRIEE>D=?< z9cw!y8rO$W4@pV5^z^6-iV8p)`EYYskxA12W4#NARInIcw3L-=b$#JV7eH+ll_TQn z>Kca6C8MGe|AmR-m`|m=G?w-95-Fg;zrY&8$AeDZ#bG!)rWhaOe7=HK$R7ok))|yppUfjXP zwtcj2a-yuOoRU+CM0*R3BD0CvJF88rNu7^klEXknUU}Oa_{;C;Sg|xG=I0N=U|oQG z`(%HSa?V>Jje9GqMI|~VW;@2 zekaR#BUm4D+6z=fr+=m{UsDQvURdQv&Z!$~jxdvNCfU>^B=iippcf?T^UA9x(b{hq zowpNO)-``OMPGr<3~lk=JssU>WIxk`&K{cVOL z%4%3+%D7o zk>lfFD$48e=rDS@Ga3~WNB6p;y|PjulfmJ>lbT)*&a$$;a3ZaA>e#`Xjh@RK zQ50cOQINapxiB_$d3g!Ktc}zP!hNzgKx*O5@iP@2U77%n1_u+l{!~6Po0Ybz>MRbA zk>*%>YunFLy%-l-%ue7mYdn%nO+gK$QN`Lku)d97CZ`hU zHDn+Y(Y`#lvOC@kzs_-?I+eEZh4bAICB0F5pBE|=bu<3X|K^S*4(_;2ATKQ-dvXPh zE_ysVI~1-;{6^bw>(xCyCxs7{8gu*6gQsHG{-kj?{jqg7-F57KsoI5c5Mjz@bEVz$ z)s|8kcaEC9>1_S5g)})Ewad1o#H^#h5~3Z@(|QqO7c>O)pqSHq#9TwpAKye5$^`m+*83MRV_yx0t$#41?cfWeS z&Qr@cQP4*K>X2cb4Fn>Z%SpYU)OV z^9>*;@016e?8lqa+sjK@hS%KH`qN~V5-x{as_%el@bV15A}lN}_Ura|%CR?eA6PxT zJDT2SA{Ti(Hagh0zVEzxt1T`bv^ko_fSW@^G6($5GdJL|%C)OSq@<)|RZlnaLjL~! z+uPgAWIVjKk?m*}C?=C`Cn+WcTu{KO%M1#7^KC8>mu~%XR~BT8oSnCk(=Iy49Ag<8 z#;Gz0^5uIfDITh5D#0{^!AEIMj>fldU%U8FJsi%~yd#LGI&80#?dR%SNG+5g`g(iN z`hJrN{_^$pC13Mq*pdPHkNv%3El2-@1$b@_f715%kI@2PW+J!!LnBjxNCaL|d^#vE zQ2DB8>3LUf+cHW+rS$a3ei@&M%gBgJ$k2cM$V~7VM-MvV=qMGq>h^TEv^%|+Goc0+ zV4ws_ThK{=hW0pvc)Wk8IV;ZK_Sjqdom1H0RDIn00u5JOatqA!zDsct)Z6^w#824< zfF8*CCXYS>-lp&0uX8ybOUuYO{9Y|Cn1x?~qGQf%eUC+y9vN9!QQ--qqX%goL^a3o z(_Nt;y|tX_O+;}$l&t0*^#;FEee^{WGqILr@DG_ak1&p z$VtbT&7=rn@IcHhZ@0&%kbAjeN{!bL>SIpyPc?qM==*Ca^P`J0J9Ra|i(>Uq zplR@f-;P%Y^Z=WqU+B>CG&ZAuCnIc{P~(RePigP@KY6V_)h=p$zKNpyzpT<`{KMk5 zwM>iZqDcyM(DEaSBTF1@>^J}Dcu%zU@J~#x3!nCJ)O3t_fN~{wh_Nb zJ;6`7cFe!m+(MMa$D(ik8Kmr^ zsSdtUZ=n+J_)t+!meecU?61BpI`||Mdwf4Wq!m`|$d-8F6|6U%zPSEjSkhmtClrqbaZt~CIEf3pct}Kq97x;&&q^jE>lTlehQ{Z@CmBxQ zTj7f-=SZM$nxT}{`6B7Ou%R;Q>Q>6Wy)I5fq4uvsZI;L~& zc;XZo-;GF|7f$u(cTF228~@~aax*4omX*OGK9}TJ*c>0Q+!6D*F!!sz>p*>a1}53U z!tHslZK znL@kL*DK*}(ZX}!W&lk30j;;meOHEE(3gOH4dxZ-ht*n5q46^Fr;NZn- z={&9KT&a3%d|d6s(ju=(4pd@{FYjNvy>}=mlvBK-LPA}yYn{F>*V$thpY<1*BlB&} z&vS*b_pIh*<@q5AI9px3edriyE3gHXFx-0^Tb`bTCEMaGvKVx#sw!D_ttSINaB&rA zppMW>NW@UdrZOG2$1^-ju&j6e(NWu7my(v=FPeyHXtq~{ht!n&s*`Gcdy;lW{&J?2 z3GqTYI5fl$!JyS-upNxqy$jq9a{6#UpjvODMvYdp3gr#MA;m1}OEmMnSafx7; z&L|EUqtV2yY=P5L40QC(<72Jcv=uaZK8cF*^7E>{WJCBXu5nYJ-(tfbR`x7MEXm9t zM%g_I-{5E+Jp&i(NwQE8Ja356k)h4aO=ycxR1CY{b-18MlBz)kVF2prug?pMi=9Kn z+(5T1a3c_8z5K_QhQSBOr-E=5;Ut`4jmx;6i4c8?d=Da8752m`g1?2Gl+ug7Mn7^E z-91c6iR;pjyJiliwnWd*efvJ^MMPGwZD5S8G$&ocC$h7*JWa3AVt`iwsxJJo-hf`L z!Cy8}D_wr$b7Z<64&zCg8;FBuvj-tEbcUwCQ`u#|3Tdn8!kVb0)ln&^$7(d9GbnN` z8YR17=Ld-_*h8w>JSdrVPQYebnS^#uR+<>*xH3^Ej39HSwv3!LGwi=g=_{!% zs*f29xVdrTs(sI_U;ycrl$6OaF^9*;Ocv@E%82PzfJo(|KM>tR;sx5*kl^5cdPPO_ zD!lD06GaO)E66C^p*rJd9d=V7X5$GBI`xkclqIAAm$0KdJwW%qYeR^6+ zHmz9e_^9deU$8={T628iTbJL(R)=!oIE+4NY0JOcNfFjAWVe--mAjp)&o{H++J<|L z7N(2dk&t};&B&x4t>Wg^Vj$3l(nZMY`66_`yETsQ9T&H+EFDRjo1zPhJpf!JmZPim z+wbm1PsJT0!o#2%?biAYAtEUrlh`=j!`sPhfm}YNwWb zb4kNd$ZDJC=70y~2ac-j>}YPuOs91nv2TS94Y?eFl9G;`o;O!*&+wp(@;$3_ob#AG zw!0ew4nz2nwNX>zViLRt-11+I;M`pVJv^E&IM(dMbTZmz%p4rBxVY%wUF+Z7^JGSx z;;|%O$g8R*L`OF{jI?G!1l-ElN!fXL@NRA5@zd6mhi5C7s z;CQ0mYHBDW6W;0z&4u3_7B<3And%lGJNya0kK;8$E?nTfb_FdSeojvEw%C023-N5a z2VY@9|Cqa)DDY56aBHy}QPC74e&TRnxXS4ZM<)0@-tqM{x%m;KCdg*|ByS}ErV?4E z@A2>s+@_Oy3#~#4Oln>`0NWXbPOYl@wyh#!tTiJACS+Vg7@@ns@ke;WXJgd+2f~1V zbxM0Q1k|vHOG4;VaM-S17W3SfxbnRK(;%M);L{AVj;J>_!iFq?s1^6%o;LZDkMLI9 z&{*;BAx&(ubcM=*G;&H9m4N?-%@047)!U0qVrecrOD_V%_+ zwv*Jp0-zya&-7uN>1O8PAw2K~xsEsI=a4Kiu)gr|(`QAO3cPrK zx#)!S^p^QAgq-8;MS1RGaZyp;-Qv>H-qNI`q;g%xV-v<7A&N4RlWAx}a@G%~L|}g> zCl@y~d~kA)?XcM`zXfq*_BFN8IQr-9t_}5Lt=OcbtTU32R}-#xXA|5*{vfH{ z?LFE<3gxev$!XpLw-4N(q-3;L`gZBV&U3F%mLCt|hD<~4vGZk9KzzFoIM~`Zk6vj& zeG(ju7&-Nk7z-=jCZBRtG{VrxDB8kwHdj0bBnx&4=vUUH%HDjWra4)ycbvb#bZl+y z=@u%(qW{doLIK@l658ulA43(du7OId3zK4I6m!1T={&pBcc003?wba&^-oTxvv;vb!2bY1cd6WlgMgb)NN@zSLR$_87=B5d zhQYJa74bN%oxgKRZUYCNlWN`xWLbZ2I|zhGXs!bt{QUs+W)3aTLxGI4Su zAKN{f1pJ|RxQe87|Ip%7-4>UsqgDLHEz_x9`xkr+i2%}{@F{oz>5t=8S0m>Gs&Dd- zY^cqkX*Q8RP*z zg|d7B9-y>9H*|d5s=Fy-VR1YVL&?aJLt*k41_V8lz_(bsIH;bUAF0WiP`7%^6{uRN zCg?{H^L_ce=Xl z^Tqkf*B<2bclZxD)li5yp#r6nxGR0FEo^NP;gwZz7E+;%!poZu5=_L!i~MsK%%!$1+RDWds%}PSvy>xF;-dH|s{4|B(k96oa($Kj3d+nY4<~kq{ z(W@i`X*v-A#F;zNe?ii=4y)ry>QO2|Cqef8|6l>au&`KISV*?ElM2lG4|Xw?`XE|2 zv#=21{AjMSm7=b!R?bgHE5ljj%NhAerTYi4kBfMIgr&%*9Fh$S1iQ8nCOmg)DF8MR z37vgzZi2)^giIny^P~2>PXSzo{F(;+akzb$*>}^A6 z#2wGSiY|ADR{2cQu&D7CJKDASLBTlhv)Cg2(V2Q}96`e(LWw|SKY4tJuA4eII9O?F zB5?uz=3<48Mb)Qo4RX?D__)d%ju4+euMCIY*o$ofxQ3r9MrQi;nN3oroGGb#aI=XE zHyUJ!J3rk8Az_;tTCmhQ&8mvwn@C6uf(V(Q!u$#R_>zvUO*&0 zNdPi3GCtG4hkDMA$!|pCNTvK6tCO53=mf;X5#AQ-PHa%n#!tBt0;u0=*;MsZgGj)v z8VV=ziwPMv)hpS|nG}_ro`r(7!pXUR>R^oSkcgJ%MAk zJ9)5$snRPMZr-)7TG1#fB(idQ$>sF@m2vkEt!hQl z(LGXmpKNrb%;W9L(m^+dXhfsJ3Hc~}_c>XvN(^W~Y$tQ6forG|-#c720EEzEVPo1{ zo_!{y4tr8hESHAgP|?X9f$w&9fqZlr`Lc==m{pY|iVsrYVIkt@OJt z0FGra-<8D}Q>7)8j@ypOIkgQYsooc!Ni1*F;Ow+Okn zu4z2R^yg8LkuV%`-@bh$A^BQcJDkb^$b`5(Jw4K6y9?{jsYQVLBw;C2 zp4vi?1eH8qlHg$QZ7;B<=IIHFLKyao1;b|(AK0hD@1iJ#e#ADnY)9DG=*r)e!~)Kx zet%|TVHB5=+FIL+JX}huS!21h?q5Z@(O|RCe5I#J4ouNdQQ1+IK3jw#czfWCi13mX z4GlVc@O3ZUogYX4GaRp!w5oT*@jB6hYtC{3rrjldk$_-LRG)cna&J;*V{m&2%os_y zdJe=O@Z8Na#TSXeM~NyNIrfK(xB zIwf)NzW==|*CUWzA=%!&uaArzFH*GpPPAvuPIx^ZQiR){5olz4(o{W?? zT=4MV>6wC)0`^jKJwuC=aiUQUD2U(x&kyo1z${Nsue7=k+0E9NC>GgAzuPL{hkWYO z)h*JJ+K)&>`R@zzN3sqPVA<%ai!OA1B@z!+7_NjHGN7;(4Xa0?MZb z>Z4bb17h317yEbhxvkz%IREEugV)5)gb?}9P5Jk&1y}$i8yjrb*1hQ`&d%`@GfVOM z&pY>dBK@7d>hUAx`iYgsYtcyWe=cbf?RR=a4L$g8|MxrVuYiU9{6Ck4FiHOvGW5SM z0{*T+`hQ--|6Y(-GsOS?wcY;08#k$~dj1K=-$h8`0e*ZjNM(^U*Cjg3uo zb<6eU!jErkY#13U=f1HQ6&CJ|RfwWGhzN_8I2=FNQi2WGKOg`Fgh>=2{}2W$NIm^c z577Y9r>;f6f42_a&D}ldfsoOydyUjr?c28wEk(u6-Cf7m7xy_>$lfsg*##pqFA?&KqA*ulT?GXIRWT_s(Pg#u-!VZP*1fyZdVfg=tXshndd%sogIz=EI=&;s z4H&=p?Xfbo76DmB6&Os4Oc#{Pb2e4~1}p{> zJ_AETWhEsqFxzfk*FJ-Rd<$7sT@4Zx?)u8WlLdqLJy#2WZFFP`?Z*2c_k_>$W%$p~ z;f}*W+bIu7{9JauS!%LxuBsBy(ed02K$iOnBy4JnpHjfhD@d4qygls;C1SDJ5aQx` z{MC;@2#k!w!-0udCK=%1;Idn1MnuR;OCvXAa61o?GA}^^!46Sj;qCPt zQRA^x9{a(vavpj435_~S5Oe+e9A{Ij+0hEvo^IIo+z>9V&jEIUuy89rr@Vwp0 zHj~K@&u#Abk~NK&J8`kGO++nwZUM9t{IyI!6@s7qaDE za2#!qP|r+-j;Nlko!#)z&}yxj@t;V0+2p7wNJrwgSnu8u@Oh+OPv%9|S~|IatprqI zI!$&(H8tjgTLba&mjjNs8O_q*a2AaqbUj;x+>(Wch90`V1jy~uoCQdW<6po88$maJ zSbZ_lwwj8{&91pXBN@mdng$FER*O zA+jVY@P((6PIp_K#g1J>g3HuyXlzW4Ao9(Kn)n53O8^zis;j#IOQ>aZ+`Yr*W*u2E zF*yYVV5UL9=qB{~5W!`sJm4O<2po5|chlHCxY^jOwhHCR$g<-vcL8us_w9#0Oij#rBGB`g zFBmLph>EJ#-=?>^^0!6zgX?EuVd;dtAcE?krj$xuTVJmiro-aN>%@={Xuef|boYMBBHLeSZT4vqFjAR0d;drGa=SzoJQnM_ zyBZ+TSz3Ao@Rh+f;c$}L_#IlzOf2341#l?PogS|CYxbE)YE?h!cs$+^lIKLBLPG-w z2_N4Ku#^Nh{U+t+hEn({C-)n;N5Z2LfsJEl zd)t2yW@!eDllXnu-WE-jU~^D+>+5lbXJmLT)>(kk1_fpDr|nOErh?quR4zwtd3oKN zqj{T3n{X~i0Tsww*;UG4>tNwT3IX^Roy{ZP*&$$@DJY=Hx99fA3JcFWmA?6*WGKS71u$^B-!(9u zaOy~Q;h@WN>J{n(cE4xCN?E}9wFV}77zzq`8!I0wwd-N9e8%w!@ymk%^Fh%Y1zpex`p8s2k% zyhX+te_aA2zBjwDLEjjPK|Hju3es$;8nk6YjeJgDi}Pb$CDAA#39UAmp#Qkwb-(Hm zI8$pj3XA(IV6CLA>dgw*2Oe-dbp_SZuF7K5;w5hGTx0?-JA}7qfm9E`vI``svpa9F zn`j3V{2g5(k~v>y_XajaB}GNrO?K~>K*q)-ymYKlEU^QD<~X%aa?HuUhsVp z8My@otC#2Af*`*#IyL%<`|chjsq;hlMg~*^9~FLZqeVbyYqPGH1jK6Fvju~B)%dp~ zLIU8&Dt+VAKmna=`}3!!W`r+k4OU5TE>)pf_)XAkc&QySkt@Ebe}rHYen}yIR?}#F|l7Yc2szRD;lZ z!sE;HBLQb4H2m8S>6`$C%7K!B&R!U1+g^b4NDfAe=24a;!=HSEvEMN8;kDZf~z0^S2@R#v6}At7N9YA$}G@7Gn{ z+O(ztI>7t<6&B`?psd`C_DY+;fDXTeo#A@>6=Jn{w`6#2f#o)UbX{72H77eq*9uho z28tRf>9o<&(fyPxJc(^>FUMSt0Q-NMsvr2@k-T-vi3^)@`+sU!Op3fbWF86%u9@j+ zaD>lXf`P_8xd_W(lXMCQhQxo6&G_Ox33IvA4dH)>d3$J-Pr0$dE(&+?r$!6%X`=^8 zuKnKuK|{l2G>*TTxq-mDhQs7igP zV`Gn&n;iu`qbejvF)%SZ`EaKZ-E{i}s>&hc92t4Ec~|nsSghB)IywTgv&l#be@()T zfMjW%Z{H9elBD3ZA^-;ewQkkanD2XsR{+xO8ziLnqRPscVU=PEz!^b|w-`f7RrPc} z$M}6tXcoP92*7w$TML(#mg>xFr~Ije3TDYlgf)(jlH1~4KfPg zdi}hgNox*xzLJL^=i^_*><~>&Tld`Xdjm4C%Lv|HQEjguy+sjb09RsWaU!~bkQ*&Vd#$ezya{PVHSqRiF1 zUx&Xy=1Tcj*PlNDZ?h^sz5`zAkDkg?Zf~!yLEGTCbEIbalI!sdLQNVzP*SkgeOxV1 zkCRmgh3Y880gjR(D_{KOr4 zg%u9;EO?p{nF-+qGC*B^F;|(RWKQQ-L@~l0|KW5Pmq|%!&!RYT=L`cNj~;9??*>Tl zfcmH(-?ukWQ9?x|zY=|*zaKO{TRS^Z6b=dM8iMNloS;tsIeW4U@<$`(3|wgPNeg7s zBzmAL@_l@uve(7oop5^}l&zJVojFYAJua@Oml!;F+dyRlcKl2apza|t{Bf038P3ea z^RHQS_~nn=Y6_{m`etVnaH{g*KBM&v^?8Y8WR|#90P64=0xO zCajyAEO?LX;&8G-QEiitO+VSJcRW`+Yx<%I>O=K&^~m-p&>`~AN2}kBa~>wLbWhmq zGWKTj;_2rv6@Kh1Ju+XEPMMtK_V(cw|;tsk!QCx$E;2PZB-CcquxVr~;mjnwC zG&m%IkP zb_4n<5RDBG5Qn8&tB2ZsmEkBnc7XW|ff;QO6qKWSTa8F0IR(u}-4F4}$;p|S?Pf!o zXA~&$zs7zNNNFYtj@bjyh^_pmB-YSy<4Ph~|D5&oViHa6>HQ3=;)TJwV>N8qX0gs@JXb|FhAHMtt}9u#zS0 zuWu50xx6T`gFQAn$^?#a6ckq>xmSsgA3ppZ8(SR?k^EQ3qLEAr6|?p2_}s5BU{z~%~@ijR%07@ZP&gfe++rGlPH{uME*Fp06I$?N!_`TFqY zWd3rd;^qtz8h!#dSn2j?1tq2{7@pan{f+u6hJ}~TNnBTOSy^(B38=Q6Kd{MI;+rth zcvOG1G$=FU0YgT+qo^-{>_NQMCSX(3nHiytk|0-W7g4uGt!@WHo z&%s0;ZU?qDSg;?z!hlCVx86frK)kQ_WHv-%#Dqn;g;w1^(Njz+b zYh*d|-!RDNog95396hVk)4+r!SjypTnstjOa)kZ{GbQEs%uHfz>|U2Ny!_|05Y|OC zaX`QS@FpGaJsp@4_;klDf21#C2+WW9odp{J*}pu_K7sPz7h zVns*@Ox%epwAuuKAQ{-m$(fi?%aNHrpZov|l^2{ej(`+Oi^6PrU9173xCoDqbai!I zr;J8_%%9o(j&D{W`fl}_6~N&&7n@U|-yXuTyR`+AP zDa{|U)6?+HYJkkXs)|+S6ZonYbFdSDx-y>uRlk}LiumNCq<^%usri5f~ zem>ua1$_yLxAuIv48s9mj(6>`26h0i*mQS`hK{C_mSK3LNQ30x5HFwU)BGnT=^r|a zUr0#9SgW?bjwfTh!9F`T;d`<$HAQk+H|X%%wjBz^`1Xzcr;?SiDOM0i0@#)1EWVSf&X^2f?3Hse~zy)%<~D{8au6FPB^4 zbd7Hj@ZR09HKYIL9|ZnXem1&_3WE-N@+fdHMzi{{(eX8di}IAfYw~t$dkQy?sNCCw zhv#p10@fW1I@k8r)*8u{lNaA}!UyR;F&)or=v%j)e|l-DsO0Ltz6c%F+f$OHET92w zA22k2a5&}8h?TsP>g^w*zIUbg&#?0e@7-|LqV%l}wRBMoku-tpTWpM~RegWANG{k< z`Hn&NcT93uq1wQ394syg$vu3(ex?T%29Pq6U@>}qoEL6JKPk3gIg`XtjTDrW_OXr( z5S&0E3V={9Hhe2nj^F*e#L-o@QDD3XxKy$!=jP@deNP`{uDzk|FpGIP9abMq9s=zij~(DF4Qc;)RA6P>oI zJ~6*InXnu|+4A7PfTiUX@bpxWlbfA3WeN!N^}hZ4m%$tVtwfQ*FKGC}Hgq8G;<7r` zs~xbut2E;18)W6^v*l1!s0jU6ErOdrvTI9-;2D~w-&#FHDv!9XuI-F-dYe+Xt!80&7J-z zwCde{=D^yup{xu2-&5=-bD&ffa9E|a>Vx5af`V%EvU-M0Nb9csgS9SghkE4jS6Yjk zl{YdWSWQ(l6Jb+luHpkELtkt#iBlGj7w%S7c0eadJ>EUMWeb$Y?-0cD{@9kLrjab} z*tDxq+T1tzi}Fe56al#HC+iap4kXp%H2>>~j|<&2+MQn%Y;?|%*LHS394^$Q28M=) z_?55@bjX2P`RM3~pU8b-eqPY?!W7ip_g6ru6AO!`N`Q7bbdjh9^Dw;LB4S|w$ZO*vmzwQJ6jq8X7P|W{+6#ACd{}*sT z^FQ~DbA$ZfQ+Kdi=>`06ydh3lLP}E~fC^7!d~y-B^@s}pgAD+{!R`xr{`3Fb`~Qtj z{Qr8A|M$b}v>>DllGN2_rZ3e`dWMF2Z;;0>nl9w8!Y2)g8UF{Vm<5;Tud%em{I3JA zOdQSIqY}EMivf27WsbdBQ3FLDvOL=#zJCii@xAmywg1ltngcWsmt~Kt)gjYbj|2lU z#sIUXWDMSo>m7RMMSQE)oR!Pr@T+|8zxwOHZVL={+7OqH)se_fvzJRAO!Yg+c8dyT z_<7Kmg(J+|ikkR;#e1fOhL=PxL?!HHY`-;N+C3TKi>?IRQ?_PKczzJ+rv@&1(1$WqPHa)-TK(8-_$B)a_33n9Mi? zghG#<>EAcE(N^VT2WWnquFn3iS%6m3j%)n^)%(6>q(6cGy)Xgv5CA?2$V0^&lS73g zs6K?bL4>R%aveTJ*F`TM5>iw%b<`108fYoMMfPgT1x>-p1~z8+?;U$F>gb+($oHkqcNZ91?JJ<YBI|zX11N2R6)<4Fb%gu@Vug0b+vL2 zA3}Vv{tez-iGrj!lL{fFU*Wnl2^jHxVkW%K{FF7>K}Q)D6gBoiz{PgrTg0G;;RM*gOa-Kc)zR-e5>K4}3vLOf}UA6oUPx$W@ zPB}{MMYo*evq)+DRDu$oVwLv=#e7WrOw|ClZNyOyhO3J)|>Un*sUx_AqN$a4_W-)_4{#PhW*L14uKHy-04`N?+4&5%#6nLHEx070ca+`;JK#idF9jmCKe`$ zF7de=wrK0I#TIcc|^Iasq8<8zIF*xqGiWYjpEUH00a#HLT;#$ZV3QqJ#2$or<( zQp`ibJITg=IQ8`A38%{D_5@@m^hij9g!tNeM&ZcrZ4d6%+Dh>U+foKW9Kj4WnL(rG z3*{a)bxrKc9Q99W$|J&?`zSR)AGNgHf^C`sQoISLNZ6pMd&+N`!qi^1ppeuzcVFvV@~ndNkD7}1JPXSOv(xU0 z-rd~z9v%k9Js58J`aaLr?@0Wtn>BDI2XxTxG33wFl9uzJ?;h03pDiehq9WI#p%eM=f~@^Lo_yA zzqR|lKQ(qOH##F(WfOVcHh7Uw2~WOIAO9~RCl}Fwh@2k(7m+ivPi4X(Uf%gjJEvt7 zcU(vue>v4(CDn(N+k1|l3Q|(a-=R=m5iqy@r9?A85)p`;`TdQQu*{ zUFz^Bu`OS%Bzyn9283GxKTd;opQ>tYb=6N+#%!~uM}I*%+V*$@;AMcgUhhv?+?0L- zV^7yS0Ne~l6L5oYHGnd;IB(0Ts($M$^?G@_2MqAlwyy`Xigx?)lm`b_`Xb=tbJ^sM zrnKCq(*P`e_xBGN^;-+8t1rfLx^I0>B_$>QtvY38_vdEU+}zw)VnIINuoWZVwh|78 z5`aq9B}^KS5Zpa~k3dg<2ub~GU|^1JP!BY2$mXZQmBK7%M*8~p{@pLW*N;k-m88#N zCB?;{CGkDE@9FQq1;hH%^N)Z`aTXg9;mGF^k&$7SI>H9Tl%Q2p`?ZuNvjr$nCjGhs z_Df*edYn@(25mmFCRf@W9{bMf)BK8xWq@=JdbKwKW$zesXU1a)c@=6&B043%zC(6L zuV1dU0-d(Z`}aIVcO5?W7tP`Q!SFA}xw&v~Z~%%3Mi$uFm>hP=o_J&)uUP^B7udw` z$08DF74yZxM+bfhS6O*LCeE`{jI~ONpfJ_kkl*_;Ki?GjbImjwNvl8`m+eyaY6EkA z8Bq5b;_i`vmEXA4`xQt0g6q}l;=1j0V*?YLh!X7imXLBjK*+F2m}Ap&cHQr z_q`xpK+GT@8o8JuG*(_fgErX!QH$R{zbn z0KG@S1Y@DZn2GO>*Rs)aj2a2si@fr%gI&!NNxtQo?YnhW29f*0{9WcBeI_2*tDN@D z;(!0>Yu>nwGW~-Tq`bPFb@_@QnL~<}F4};~9_DgD#_V`%Hah#&iNh=WAjF-vKt76p zF8aVu?oad7>3XY*+QOd%*{@bUcG)qa2uPkE+C%9PGeNqXtcpU z71pquHhJIrtVd&D;ujYdLfp;|T9e`6;Ixvwq;n1}EeY_`KZ2}N07|)?zAVzWc(pmQ z$>+CSKAB0-X9`kYLl+v@l-w^*H*>v%#5E5-X=b6+r6TVu14%It}1k$~=DcQusak08E3x@$hy*JS zAkXiKKVgp0vW~RH zL1K}ps3>WJknaPB`w3xn@*Bk;Y@Tcb7XS?2-reqArfy+i#F3Ge{gk2;kgaW5H_#i3 zqNEN$c4}CSH~Xu>=dv3;kqM-)ofoWm{Q-Pw{0=KI$BRFeG8O=#7O11d+sUu*xE)rO z+CN*|Uzlmk%s6rbBF8{9F84}QRuhmq2(J8GK`{&zJZem?EZ&>f^JU zyI5Nn*Nw0tEY;B`5Hm#=h&wq|r8e=x!|&cX{j73w0*Z`tr*p8mn|omq40VnLXNkY$ zRGVp-nE|JTiGgp^H4t9deq=e!V_E!*!@m%JiyO}hF3nu?2*AZ5#Er(;o8tU{Vn9TA z)9QAOB+!9E5=V8h_4xN=BTn2appY#6kgz=R3-kjVYN#euVQ61pk697zu@qKzjx8|q zl32#2D{fa{WLb-gZPpw$J_$K?DA92(zC+z! zmLaNY_(RFGLZaWw+}tk8F4XoWk&%F4Xh7fCFw(;D-4l5%F2Y&0Ji=$QEv*P-XSbuy zPuz4W9eq0bqUu-=t_(C~SEwU+txOai1|(!cH+NChXCWI-H%5b#t-%};BAv|6F<#Bm zaRVk$U;|x-CnN-_0j{AW8_TN@3$nBq?9I+acLxxVtPeOKr|%kT95>gRf+_4a`p6J2 z92{r5Z4M+Zsiv7Bp+r~^S|^()(9%@!+Yt}}ViKT@GJXMxqmuV{$4VqeNb?@BsIr63 zZ%5w+vJ@O6twp?tQ+VE{@c4~JKw*|7@3ywJZM-PlQBO&6(@2IbB`?d;RVM5-Mo2DL z)bUoG`E7y6e0$9KGMAcXbSM-&KRp^XJ+^Zf%{(o1!OE}iV$u0EJZKZm*40ny5QdRe zgin6w1>|Mn$#aT#++07$LO4dJr}x*oq+^K_qy)bBJzLjo0J;n)44 zwkPQTHj0B}Cu|2E9^S|zXt*o?#~Vh~4iywmmho^pTAJu|ZiizR7aw46q}nknpBw~c zo35~S5V9O#xeh8tz$+gBmS$~Xc*WO+M877hDD`NS+j`ryR^f| z-?6j2HkPC-1;kk+BO(SCL$cO%PC73)*D)|K0KU!R0YPJ9BOm|V=vjVA33AYv^z_G$ zSMwVZ78Y{0YM_W3ARCc2$=ik+vR`iQ@VfpYRf64_o#n9HN=r%#LHQ#xvS_hLjr7x> za=^qs`rDq7kwN6+;&ljgmN+mh^OU5d9?Q>siC;k_lIX}Q{&4&AYF%;BYdlm^s=Sr0Q*xIK#E`O;7;LfHa8YnbFGW`gQ?x3AVhoe>!7k;=j?wp{m z1N2OWsoCZJB8}4~btR~860(|ONwlR*=jHKK_^<16+9PT|5t#Xc!L!1q6*+W5EEK3k zNF~sS`6Sr>9%z(2+#G)f0OJ5J80p1{U|mP^fB;MD4Ab!KIcgo)$fqDZiiM2N#*E z_ir2~G{61)s>2reF3}zI&a+g`*=>x{e{c`w-hS7ZiTtv{=JUCx-kDn8QP)4M>A*B9 zL96=sEhT1vc3Ri5Us`v;-9XPqGZe0i!!rC65Qd{J)^|m>xLwoT4m1{UTjtEn{RpjL4<|COX6;2;^|Iv%8 zJ%1C?l8N1yNZWtE)K~D zhPD>E)TEmfhxvk^AOSCtS+^AeyV~CRYSwSuBl|=^Q1B4Z5H$UTQ-CWZAt50rH)&IA zigj>kRPoyo>$kY?34o#+MWGRp5R(#<#3+ej9BplFeZ-(5;q^xV;SEZ0W~{UGww-zU zModf$AFC52ADt}J(bF^!%zYCQx&qA3qo}`M8qCO&JfpCX2C3Bn7v4Hd^ z>J&)K!p6qu{~`v;0btQ!<|bhZ!w)ZbDNY8m&Hp+28h>oDKQ2_w^RluD4{Kf9-rU^Y zhP@nMVq_cv@{c$yQWL`|VNwyX)_Q%^v?P}=_E5QPw2*&#dpV8o5M&f8i@`1xO?1t2 zMiza3{m)#j2YyRsty_eyGirZImJ>f7s+^LRw#bNLLiLoiOH>!Yc7daiSziVnph?U} z(kMGEjYC^M4-*@PC)tZrF2W1d_RXdgxNyxx3?S9>BlZ zQ}!YS4OILOtThsd`D|WgtWRMFGbNRPynuJZ-srd<1Oj#7`MqUj5%2Y3m}0X|Aj;4qK2UGL_UfmEw;^pqIT138|j^&`30t zG(XMn4am7kWjc{;rq;wckuDLj$^nTK-3C@s#J>QkCPixUBVPc=a+U8QIG6wtPr`(w zWE6PNRK>C>S{P>Nw?#ZKGBZ^a*&R&WRh+a-j*%3h?*4xGYQg7|(GVfr{JaXy!7{WxVyE$n-t?)+mZN zx2SX5ZJ(^LwKwMWf|Lc8S*%*-<>qy(l8z9ebqhCcw9+Cu(9*L4s`( z^G5KygFy)I{3lqCzmu23YC^eCb(&6}#}{pjbUG6OyTCH)*ZyMYz_^Cs^|Oq;T8&kL zAIXBC`Z}ywodCBlDB#>h6~z%!e}J)snW_5Wf;brRaS-$7-mlLz(>*TvkB^hc5f++U zojS-MxzOY@Fak_!JD#rVi1#zl_`np5fhok*<4s*vm5|$TFWAtew7o9P7XgQ45?6dZ zTgL0G(?G7+1gH*^=l`l;(`}K!!Lm&HHLW{AzW_CyRGo~3ltd83&E;hYw*_sh&iXR~ zw$nHSmc_-#b8Z>`r=CJbu#Uc-uL=;$z|ZEYD+F>qcYi>*rY zXdyP#BArN8KdCJKj4Gvf9w5m2l6s>NF7DwG+l)ejob3sdUp|0+fY>=gDW2;o%_C6DO5;$l@b~Wa~mIOuFO-3 zEtS-V^x@czJd$mv<+hW4ghnarOuE@qlnY4C7Q-BDnV!vZ{^^+(d0E-(nb(d$Z1s_J zDdwnsDp9X@$%%1#(;PCVs~x{s7Z9Bp?N~TBDcx&4hSO@Wfyv^&LzLuu=C?XKtvKlg`rfKggJn8e8i7|wC5uN zJr|$TMo&Zp!oUg`md@Ayc_;S4GmlBpVhsrCn?eT*BN6LH_5l@eD<(%+iX{fT-~TCkf$q} z1zy?I?(H+~hlDiHKE3*X#_%t$84yVI|CW{RX94`KiaVxN#66IPiuod+Rs~?+3jA;( z0CU8VwnC9rD%K*?N4oljKn>%rjir1DJf_6*Q7 z(vsYq7VW)$jf?d&)O6$87H?mjIiy*k>l6pz(W7{szMOikVx59whxC%&)Ij|ogG61| zQ|HfjJ!|gTSI}EK+mZeblAX;WYfBYbIfsC226&y%Pxr`c0m=1AN$=jK0g2VX%G#QR z3KAL--!zy=nlLc|Y6?Fzrg$5uFq%jI}j5Pv|LXDN~0NQrZddUfc@l? z4HKqS_dhuVEfA9?SLi~vS)14?dIFV900eNQ`lJkT=trc0fNwawHv!0oD#CufdsRSh zBC^2R?5g&ji2reKAMgu-fzi-zCK_nK{JR$>_Ol@>^gaWA)lx=_yDV0!Ha8Vk83wq( zb%3QYov6P0*j@?DIy}QE6;TvPvb@n3fnREl{>#ZS|0H_?M>`@S;y2V>Lu{*ez!Ni6 zK0py)4{ge95fB1_qGh0~>wYb8wGV~h>G@gKo0$;yA>@aohzJA}kTQ_)DP-Zrz8-sL$;;T zQQ7X8qXPpn%F3szzo4=6vFp%LS!KcG*5wl#A|hG0dk$!3HhABrqTobms^|LW$ULS< zQPn}A^6+|kd97Ss1jRQ38HwYxVnsS{Q)g@ywV=*W-$fe;oZ(P~XtFkHn3~!MR z2nyIwkIEIuj`gkjrjd@IJm3Kk;iI%5>dGwnr&xpeZ z93GQR<83~7Dy>^A4t9(*&yKXtXV&N7p&VH2eA%0e?gt`~pj;#`c5U5~VwhIt`-`V*Us;%mb)fj=yCB4WP zzju1$wRJvASA}b>>gqXFp=Ya?HoFHTl1ec`zI~u7FPgp9iXxP6d#8rIk!PEUqmW4#kCE_EuR&;S5Yz zciezmGp02s0hj#~s1gM5RXzf;Q&4L}c4NokaWqt;AOMT4e%;+qJWhW6_;LC=TH(l)sX0cm~<={`sw*FQV>X!SXsk?N03{5d{DRV$#QTaP$DR`0gLd8<39jrfbKhi&{kNATt)E@=n&m6>oHf6%MQVOSbX zDL4YchqbUCif&QJ#StA5Q#xALCLrRcV5X@GJ3rh%5M#^aaf(j}&U+1j1X>@!ZG@DR z)=Tb%L1r^aNKO6J-Mu~=ka=};1NR61)iH1l4+{%=mW!Rpd@ODI#LxG5!S&x*VJ2F^ z`dNH`{Fkypx*1re2CSDIsqvWcz`3GkDFQBf=IzLE=TP!IIAUA~L_4l?)#nb2)P9bq zopS`f4a7(6skB?XN!RBtFw2)E&IF^tEhE4YJvurTWjVawML2<~DOq}w zX{Q6niNK*S>3v7pJ?rTj!Q=iQP_G}3jvFw588`v|P%di*+0K3V$|y0_GpPXDC_=o3 zL_i+!59yw}pCh&a1GT0Xjw$NImBm*@=c=$Eh*XU3O{vr$9Zin!TajxqH>+5OoPp>lWV5nuMezIA%VN6QCa>>z`Gt2eqb3 z)z6=v!N?4tG;HI233y-x>f4(myjEKPX*D%AiU2UVTiL@uiuug|br|SnHo|ZiY=a0S z5M)z@9W;Q`%oCukinA(Gg}}2S{<+pdFsx)35+a_&(iffX*#~)=02`k7visk>fJ)hE z2Pr8y-S-rnrf`^Hg9l|Xg%L7tZc3K~fn=412$$dA0}ho4^eN2}G0;Jw<03HcT8ssW zq9okw7S7Q0_V%oYit{mvc7ggf5*Zm;{M_kshmq!Rb7M1S36CXPc&Y|8_y3ftUw+~E zG8lBn6jA-8%D##qEC#4zr20H3UfDZ!4~(H#^#sUwi<}9x%ZoQoKDi86G_YR5$MSO z(xjwBEC-!~I3D*o8+Yp2M>@KL)stKd%ES|U)bi7pj<%W0koQ-d9rPJc--`SMIlt>2|ts-9&axtGJDg9T&DxUkVhEx<_vf zI%E@s8Em0>*}(?6ptW4|*@EYB!S!%0W9wM(>i7L;(j&0k0#7s8%r5p+Kjy?y;E^pJ zY5@Bts;OU!%l>Pm?I)w^!oo6tVlwy}?3cOh7Sx0C3G6!O1A^oC>(&(`8<;jGlyHUp z&f^O_1pHrk8~k7W>Mlv5@}D1XMt*Q2D?theZOLB(31B|t!_^526s}ypT7OJ+YG{@* zP|krwgQ=e;L^~tH3d)&0s0)~hK*Ds4pZd?1UB%6DTIJNJ5^+ICvS5ch^x@J7kU2@M z&Qe)sn0l_Y3_sNn(=DExNL$=L2E)CKYueh?fmgx8aSnm9qNtODWPgt= zkh8JwipGZ3K}oHEJGN6P%D=tiXDO;zZwqFt!PYqFs1zdN;xY3f)VMDwZ>|Uu5U&%p zP-HK2{AT5%+@;(6Gqwb{bikFHPhZ>2kw z=}~d5=|=?uqLY*9WP<3}tW=L*HllvTVM!V*+I^g1{XW{-`gF(Y{)nC#nOG`$t%#3n zu{<@2JZE);e3LqSHHZo6D9Na^R(#`#Sn%EUrF&t(xemxszT&e?DE1w2?GP1OvJ`Qc}2zE zaIrNUSUx-jySwCP;)iD%28K7Ns9YQzFf-9Z#5~u^!z)1lJ8lJHgMkRaxawmZokz7O zlW4nWu}Ha%m6fR5_LgDyW*7X5oUkD+0}sFH+okC^Om}zpRzl9hxQ~i)gM)Gd^d2eV zl)%sw+TGRF6*Ne?Vqf1eI-;N%fj~}D_H#dL(#RZAKeyk)hDP@IuR>MlP`FUtWOdJA zuH(OZvJ7C};-aGKIF`lL4#h<`Zu^x^SQeofN~L2h3iSch^76I4#Ri9lay?1mM-#%0 z@IV-r4Q|*|Of`a4S~VYGCJ{R1LezQlzf~rEofT3k`k9%6OUqO2Y;^Q=T@p$4Fvjdh zr3SGr>v|f%8m!gxbCbNUhv#)T$%%fe2L>*T;X4`WpN#P;o9M{MU-=sxc6WD!zXhga zR2x(f5fTC$-~9A!aZ_%mFqF$Ml2%DM`JpvC3U4U{e@Ye>Zf<67Zuph#N!BbkDA_FUb1}Iqv~*bNx0SARY~C_|e$h90mhVO-UKN z#>&b1jd%u7eeo?~OY?;3OmSrT6SqwFxi2u@hXtb`Ne1^<8g+X$IyN#DC3Sy>CIm<^ z7(H;YvnSLmf!vHa9_B5Lwe2B1JUm00{lmjesj#;|O^m-zMpqR3{X6zsT`@7JjIPdY z&9f3MFu*jV5?i)Z5LzobUT)Dc{tzG7-tNLx+P4eMWBzj9AA*RLYtRAbZ(?H71YL7j z(dj&g0!P24=)y7mXJf;7J}r1^Z5bvN6v{VsHgi3FeYA9Rz?WYJ3vPyBQUKIXz#alK z@(p*|v}U<)u*jPH>yz)rR?z~jE({Z zSbfJef@g~9WQy~=)oT6Tt~Ve&WxvDdM?w3~-;brmv*yL>gVasAB9yRYj)RQr?-DNw zO}$8&3G7jYml*ZR(Q;u!TYLH$Xd4##BFLiqS0pG+TjUk^kSRD;;1|192{27^OBBd4 zGaX$2(1O@F6|!%Wg=>H963X6rrne0hPihuunq{OZb^D$=-bA6<9rB4!dLq zX`$3@!bnd~a6(jA(@2-6LgW1TD4gusR{DcFxr-z0cM;RVh}7dd7|c{7!x9mLfuoab zqxUEkU@;ba$hktqv_sstV5`e_htYkjZuqGH5)=9NYvpw`S=PD|yoP~^;$s%94%AXq z+}PRQz|w%>z%8%@GXv})x8anr2Pi$RP0h_;Qivzw-gs1s#Ag$d<=tVLde?9}k!E-- zCs5&*|DK%0q^PQ;6TA5>5AT3ZB?IF)wGXx2H?cn2CLOH{hlqfRa(gsiR__l&uV>2^ zk8JG<vBq+XH~i_)1gZW%uH~43 zuQnETYhV6nQSONH5GlAMc|Aeib1JqwPTcOvf}w}&W34&-%sBQ*(cWu_3hQ~~E-Jzo zcX1dz7e1k>_ev<=5sH5IKg`0tsq@mbiieuBAzwd?rzR496L20r7OQv{@1vrsYBl^2 za=isTOFggn(tKEFxw`V{gsGALfR5)=XM84~-NFJNU%0 z-u8^gn>y_E@Rlx`y7AXiSrv`c$@0*r$ZZjVq4H`SVHe5j;MT5Yx4t(;1TJ;={WZaG zNnjeOz={8a@3{gOxGT$!xJviiAxMO@lX`%1(vsYvG&T8+Ea$N-$37%tJ@uZQx&bMF z49<^M7Dgq3zXMH zL{u8f{c%PCVc;rseO=|wtuK$rw(8}_2eNQ)iG@5Lo-|B2SYY0NlXadG0j>nWyJxNU zmkoB^rjC+k+rSn9G)~VJBi0IRXM{t8gHY0jYu|C04eD(c>-SyWO8W@MK}eCJuZAIF z^>IrjkfIB}VS$V#;g4~zl_EXZsNqdDCAw&~*u*lJnQ7F{lXj1O*GobrYNkQB$EQpC zoT4^|mI>_|sc;rP{j!!nc#|5hXPF%t?62|7ezs71@>7L%l!ZMX6fQ28N9O}0e{CIy zUFl)EEE&_w^ZeK3B&N6;IVjN!)~kW_?#rDKU^%(yk+m`8=&R{JHD`#9?2?&MF5TDa*2W!icI#&u8PBb%kdVJ_tWpP2M=AtCTo)P; zVn&+MfR#eVe`xf+|Bm$KkQ6dXgcKbCX5+S?s0a#XgMR|1gfC*z^MyTkwNFAqa<%<7 zyAdY+v%b9A)bFL~8Vl~GO+SnXf3*L8N&k85E`%74m0nLQ45Hz`m-+8||6c98|MkoN z^Sy{wD2f1#|NP`XcR1G>cY!@ytd+CVNNfKYH2o{FcZ_E1Bf&(k@$Z&WI(cX_GgxOq z*8l!x|M`!sU>|7>(k55RWx_ys^uxhr$64<7FRasYhasE@xUy)zj`18{dCsQ(mrDU< z(zo5~!gN*t^Mrwx2ON5kx@c6*gZ|8Jl~$vL^qqBUQ15giU|9a^RrjFfWoHNUZw3ZS zi055w$UT?`PBKgYjLA{5jbU?VAh* zIzpj39a|1MC;FEja*RMZt4VBVYX=a91pHNkg2cLWFe0c#fW=szbSwiSqnespSy@>Q zs*SZZXm`7(Qp9o)Ld6iE<5R|VyM+aQl$4Zokt}5=fmW|8yw{i&Hwv)Fz(bXO@L-_6 z&d-I2kos|^)j}x!DQU;iU|9A^(2nFUZu)&UTzo+n_)#IHnqDeeN$qU%EpPau#lHns**V-6467S0hjWV_Rmi^z?WJkGAuf~jE{oJW~Mr&8IP-*2q z5E4judq+-!)2XGu-Oc2U^*!tJNW^M7FusW)X1`gQ4*)F3m!-tkvEqX^a^vNXL*MiW z788Pc-_@s`8=G=JyP7c27sH|lo3S_ZJ?jXWc7{~fG{8&SDnzMI#SjR;Oyu>qxSil~ zSfZa`fii5k-{NXd6_7+s`eRx=&c)zyk{wdsNc_9hpa)r4Sa^6)ERi4WA!4miqszT* zdOix7mi_qgZ(PpG+THFc&?CCP%>NN3#ShKP+XSIH0KikL57oZa33k2f3D z(;%i{dsb4DmAWF4dqBh8#U1+2?uA;C^sRUOgwQo9f$h1f-gTGi2u5bc>R{JftXc^u zr`71FCpKa~4{Dugtk1`?62E8Gg3{MR>7~+wEmUN3zJ5#0D>P->{J|L}9l z-%YF?4l}Y-{nvi+R436lbyh2j;qoHQ=ZucVU7faHq)pAuz>A0Z{d;HynzMCBo@_tF zzYW0_A0VF|T;D!5*vy>}Wa6K@2COeO7`$4k{Q{!IJ&Rx4!hLnwq<02Q#(-a$gaRi; znuY+%cVGn!m`(VmQv*kUXF&uNYOjqDr)as+x6?N+0iY( z@%h$qsNHM}0m{A`#u?P5;Z`Emt@V7t?BMe6>lTCSc647AsN~W=^M5P}hEN(Uo1N;k zy)w$UK0C?i<~v{%_!gI_gQ62^)*33Vh=|ZLmJMWS?)IxM^F?=ZiP!^ViyhwLHqzYO z=$#W24vvt7*AJX0p!*>DCIf?nx0GYFes&6EuPG^aZBxM_KGY(NmNqtuX&mPk)G&c} zYzsh@LrlB^aA6QB(isfMfZX7(1R5g19AA6??kj+nMX25PCkt9zb@lYx8XHqa%!74+ z$I0X*8wJHbM~$cDr{cC93t{ zpAq!XYvp9I>r$DrFRO%9M~><6Pw(Tq3y-df!L(IcCXm;na7ed>Jt^G!;)?TO#X^f! zqsTWS^)p;robU?!z1b40&MHg?vz973n#+XVhKUrJNegCmiIVVe6R|GoTP*GLBHN7V z56a>>Y4n6O*T509T#BE9E9E1nc~buRb@&TYI``Fv0D zPw%fLv|RZ$&1=VLecr(zQe-n~)*dbPrl$GHTKrxPqBbNSOg9p zz9T5g+T1Vzd98G~6gCj~RJd`>luh~#3CPRw5TK(Ofrdu|?|Vab_V~!ijQikQdU^(N zkYm^IROkBA>IvKny)UkcnwuBK$;5vtq&`V%$h;e{3nMo>+)BFJneT6cMElIdXoVuF zGPsfD7x(#E!%vfSPRzjb+7fy99SK!KMTzbXiMMhAx?bsYj*la{(t`~WZ3Xk9K z5Ws|d_wH?`fa}3{7qrT^jg3vPj`4_|`zk-NkLhP7P$?z;WTDx6049bP$Vl1E^>$M` zM@^UM+;%W6Qw|5UIpT2gI@8>C3q8ff+6`{U$(q-I*95E~JIno+5q*d(g`@h zIzK-|-?^X1#3+Fh4M_D1Gpc~#70|-{&(=gWG+e=&li`}a#5d}*^Ony+kG)^SpcvX= zW#=(#MHj!tzRA z`D;ibmxp?jjg~L;cSF#a!K>d>59nf#ib+y+AuDQ%e4`Ato@IZ3s`ufQiO9`0HW2Rc z^ndXLnL=!#Z_H{fPFH|%JeG(Z&e?ghPcfEQxV*G9;0K_*`ITsvt#y9&fA!t$i{|~j zLkJQB$jPg{8|+_RYJjvA<{t5SX}Rrj5SxB0hNk`9TNiM}*VYVeY|?LzGo)kjL;Li} z+fm_yS=~=`kwT#=81LXI-}GsK7%~xvOAi6`P6|v!dmn&g6qw4TOqJuUKCxw{}srw6)+5QtDb2j)$jfDZASs%p274{G|mjIlw{+0a0MZLmiccdpF1YhF)U$Zc84ATvM@As!?ZnpQB^rOsA*|IkvG%4QCw(7*sM`6WfQqE;Xm8R zk~&oNrN5>{^4M^%w7M$JMgS%y#&vVLN&>_3mKunK^t3h6j!S?5wPS z?r5=D632ici(Q>ZH+fN#W9b2fks+dw7w7W67F}HpS^S}UFMd5eH`u*#uW;gkHhGQ~ z+Y0svvhqbXf;9qlB?GXRL*hht1{fyE8)-*0d;u6YPj56vS|;Ff{0zqHx2KEnLV>>YpScSoiG<47`CRRrM7$Ae-IbPdUdPu zeT>1PR*oUzE(b-$Tu}M-Ej~InUQxky>Npg?$K=+Z^Yc?MS++5?bh-3@g+33%iMC}? z={WOXHaq9UoqnwNN+!?#m8TH|(`X{3n|lpXet)w6+Fi^m#*YQ37VXB^xF)Oc=lS~h z%%{W@Sqp|j^!jkW(3QW{a(tmsI(=c?AR^!NZDo$8I3wqbT96^yYT~faQ;Y=p&1Z8l zMATJGy^no1s~<)piae-u5#k{&oa}QnN3h)t2$9Yk*fjpo?F@skE@n9ojhQS8T2N%F z)1eeMXOFrX}Dt0q}P*rOExlK&|qd!_!a;Rc&qjX~?_z z#d#|mD-h_0dVzN!4n)V6>e?cmpIlw9VJ~;*3Ugl{SAp8<2do(fFmJ1!t_84qn;((y z(_d96sB?|ARnK#O?C;8}->pmL7lf>>vosq+4j$U#BS4jJ^0El^Q7gmkK{N)G^%EVu z>GY+B$Srlhsj8ts-azno{Z8VepU9ezeI06=Q5<8m>md07WhbKjrIQkA%7Nf6?-egIr5-rFoH(SqHsnE1qVCsAuEErwk=Xr$tXugd=UX zVFs>@-tDhXkGO!G#YdLanB2Dg!P<>gT5`YpvS&t{$Su(P?9k-z>L*aFBOlDg6?r)5 z=`?cB3#g727?DQ)^1u%=4>)UiDqZ|oXk1Xq&DFSK^a}ImDu>kU)&1~YN%5Ed?e^%% zRDKJ$8uO7(H~)vD{d`%hRS9VmtG#hiuZZ-L!<PWY89)Y4c?|w}pjsaRuP?fV<|O z8FM(_kdz>w7#sV4*!t_fDxhxbAEvvz8pJIio%@gHc?Y=Nd#yFc9OFA0gUeRHMGYvVVdT5V$0W{sb-B6QfJ!akaX|F4r{n-> zpo3fbhLvBnD52U3b_%zOwREzD+2l2^H&=_(-4!iviO3?^lJ9Y1P93X;Ry0o+1XKk< z8=s4ZM_B3f#wfB+kA$SFTQ%LCR+%=rsEa9Rodqzpqxnks8}A*j*q=Yu!TSDNxnYzo z;1u6WYmr{M1O5d?i&s-?@V}sC@X_?r(8Jw$UZE0wMc1C>Rb9SQ{;Zjg-+Fnek z8_w*6Fy|{=c*Pq9y?^Mw8na(J4F0O3ren$>fjOTN)Xnxwj$zB1tR-yAxiwG;OSvfK zF_Ql+D#us*@&aZ$3dZxco+w3UD~+gxLB4AQ;O3rPMh`v_kv;*e@g zaASx|Si1pU(Hd3b&_CL+RA8qK_r(i}LVN74u`mYfy*6jnX|XT+TzsM@L7; z$Nir0>>PG3su8Dqda5z=!2moETn}iD&&{=fW9#-1rhqb1Vb}unuANpf!N951goONp zkccs`Ck>wb>~c^A`oDz43}3lp8?>N|UxFdv6oObW0E$N?i-&ZURUA;{ZT}X71ks#G zTUg$FV)A0zV&(Rzacylo91Clf>}-&Nf?I4Mf4@PK3!e|=tJct*Jn1*QA;jkoDT^~0 zSp%sObfz<-YZG6yv%~sB+QNIs*V_~6!p&EUfMO*3 z(T7e!hNGhUP4#)~bzqHz3;o4p#kpFXYRy0#LNYEf(G)-EPT~yoE&+%vKMC3=5{DH( z+pgd7|g7Lz5EM8@qtMxffc$;QrZ!uHR` z#-?6FT?f~eMjnaA^SDj9!~ipPb|dw`Jcr01A^oK_^K;y|b(L|r&keHczs(@K8(_IA z4g!7%1U`TJ)#iP- z4qioG*Us=IB=joZb@lZJ2m2)kBr+vKIze*GGxA#xQFn`=@yS5tdZIU(X;H@rR}3Oh^?SxPvo`18P1h+h9|jgRZr9Db z5{@(F!IoTTn5)IaA@V z0^YEo4Yiu(jfj4B?tv?WgJnR9N=@0FM?5#WptUabQs%cepKb6siqd;;JpJVnO<10+ z4rV1*W6>yjn+1tzdCJ1QTFQ?;mRd2iTQ%@W+7~Wi9eYLNHWU+L5cmlfEsJ*Jyj=qZA0p*z(D9 zzzoHPuLBcfi`y{`F@*ged%1bSAtohrTT^wNZ^lL2KdxOB`+e24l2Jd<#j9ip@%wiN z0mx7{`scC1L96cPho<8A-Jv+@X^q*$>T_A%Np*;R;^b~Dq<*tC)Xaedp+Ok7TdH&C zX{-oD3#M`|;bech+EEe9yhtqFM?y{AK$K>^k1TIbux%Xqj>ymg2%ABlD>@3d=esm* ze$&N^whPuo^6Qmq9*n{u>FHuJR>H+=qTjl^U&mba0-ZyQc46|q?xXPfoWZh&p#V{k z)WGr#kwGvL&e=~|1uE*i^FISRmN2LHhdoD^pXL zQXCx|Y{{b`=jXA3f55&}Qc}`${qn(Q@tc2}IKc%nG~9Cw@^DU;$14=!FGEe|8?C@E z3iUvPzCaH_=;O@?n)*OqcQ%;dinr(mx6xJ|n2(->#$2i;9b_ z59$O~34*->e-^ojm+RwoDyWl|>HGmTs#W11Erqq){&XSz^YZd4Ut~#l>2qauJgK`; zz+<#xMaujj!UhTwJ(!xd)b_Q_{KG+9tR2>)=kTgQ6)DlVbL%`_jYDVVM*G@ z3!{kZfkUg~n#zAL(e;b)?vHv$NfNCH&Gdu0wxS&?i8VzPM5+5soB5*wJqarCLtjT? zyiv85D7S(x$TB+Qd2P~_|KH$H-FtNUG5{r{3q9x2^ zZZsgm?!;-y9{>;F%@{&X^OyR%I#3PQrAB2VF-ZUZeKBXZP06Fz$DbX!rJMv=gee>_o{O40}K?7;t32hvR3#$^B!rh)w?8h#UGz@7-PlWhd$*N zX9DN>$;L z$Zm=P}@C+nWnr{kAKV*L)qZ!7E$JQnKR zYNrB>8nx4~-TP?Fb>?UTT!6;W6_;so)0}VD4U5iGm+pV;Yl->JIz&U%itc&dP*%i` zS5P=hqZ5KIP8JkmfD%=@rh;i@Y3TyPl?F&itL~HOoUOSOnh>|zrY4v(CT6S%0NO^- zd;AwijY<}!p@LfqIYfDIxnt1r&ut{kQed*(tf6Cxt0N>Vp2vLLTt%|ghLz&o5q%=NjKJPiqOYe5$c1yVw}Ah?=?|itz$O&$I8sm) zkB6`s+{hswqCV8;_`6OF#R!$f*<-TL!r@DysV!N0$*sxP!(XP3opYEX8P|Az&ovmS z27xi6F-hO!8+qe4#?LuA$}`N}9Vds;^Au7Jxwr*(r#YFTZWZeX_W!h;)94VbYw945 zKhf1=ne*GPM=(8VzDK1kVXv#QhxEGfl?aOF<|z*A-TD?1Ci(K#GWAo{37l2!?O{Ja zG>l@3CdVYl2ooLZq9B%5mN)M4<#)K8)m)`tfml^`qH-}Q!3C-~6v58kA&;maVmNtp zE$6(k=aqkBm)OL}_Rp0*QiLmGmWIIl>1EHG@PCD#m*@Y!KHX;MzP9^EEa99YlaCS)5l>47? zhp{~d6pzfzvkN@`;muFD$Cgzbf4yKK2xPqk3SjG^9g#JCz;=O}T&D73mANeR5MtZe zz)y{cFKC^_b4po%u*C`7;QH05WS3iA!8>#HF0GrP4x;K7<#7Z z55Kbgnj1jy&R`YJNYNj0vnlzZqP01r+nM><#^(|Ni(b&-a2Cktuk2s;oXNBP{o@4< z-5P3Y4$L-|E@$iiQ6QO_n3fxCz5tAClh1)#Qc{xDM23crPHjWO(w&PoTXuWxRkF6* zbJzYv7Cd}dozof0X=0f+m{#iR^L9vwH$Nd5Q7@4Swiu$Je1icXSyl#)&%gr-qZn4C zqpdxn{4NSt9GROV^*v5x>!QiaoS?!ZUss{I80)}s;J_>V2C6Rk?wN(bAMDfiOtk^5 zYuCqFY?G1Gl>T)~RYRR${jT?QBA4*nr!7fn+_lo2j~&GSIg^c?j<3Y;=F=uj70@?E zf`FoJ^twP>uHpOnlbdcL#5QDxkmbtCf$w)1stX|BP8Sm?s+$q;Iv$5a;p4LyGU%!FZ5RZzvjH!&m221@ztF@57tN=ru z^9vLMfk>-lZ_nlBrM&~2E$fGSRuGoQKnf`=G~!fn1oS+2_lEZNmqsP5ZKI(7SnT@y z`e4e>U*OT!J7c)Bly9z}Tm1*eGxQrBS86P-nrp4zsa)J#rOS5-Skwbs`#TY62gBE= zyOVT&yChfXTp$-$7{&aNpR2V(XRUmtAUNl?BtUrtm}^-Yb;DO0Ip`m*v)HN6CCJx< zsVh})%;~(r0g%y znf?!(0g3+Rg`|cTy^BLY#nuT%X6fwumTKN^RE|p0cO?~LSw!}CY>_-hhNDA<^W0p{ zmDG;{W_~7H_^Unx9ftCFwdhOH%RMRko6u-5`Ek9?_#!WH`;x$PqeHnAoBcJjFCpMZNR|bsg-;8eP^ETs&BxCSE;mc)?ht>+ zjE9T9wT!?jkVVLrdXz#fS&I3;>CzU^Qn&^ORrJz@Pq%yDZvJ5c`+;A(XPtg8VN0c6 zH?f#OZS%4uXgn=UPqIsOM+G^ zL>2Bns@0bgYAOfH* zzD%JX;ccy{on=`)>}UP$L4RKOQ7`mD@$^B~L?5}n`V`Jz$+r2@etN!(YmvJ=Z+&Cp ziXbupE}jNCp|(7o=8CJTzJ9I#OW4~nKQz4GP%Kahw}dfmxu6yZBL~)L43KVoI z^bVL0&>%vBWJGMQQ!u#jxu62?(N?1v&>XC%;B_h3NT@s_WB>j8hlu?S4;MFB4&v>6 zKILJM9Doml4Z*~-eFb~&_?TQ0k__ww%dA)YO&3Cdrg%_e;ZhxKd-UO%4cMigJDBw9 z5Uy3^KYt!gV!kLqkoivV!w#B}vCK*(3poAN-s3Dim>ph%!yT#3ho71{uc`{;xghu( zuxW@pF&Wm-@bl|}OLw&=h?3AtFWDdLgE*&W&dn;}VTTLd7ulSAvYBU6frHA#C9q*mFgp)1ZTDGK%@S%dO18LOcW{yaH8n9&6AXyYGecEbdhUz!MFjqW z|23}G^4n`*qt%Di{#OgYK1h(LkNGL6%bv5&KoeE{L;bJg!ZlO_RrGfcpZ=me__pPv z05U&9qXGQz?dfNls#Kb%CrvO!+(YrN-`u$#b z3mNqOKq=F&b!AvJ(B!Bt4w5N| z{qBlGZw)u|hSS)CYRMtMRLekaFZ!^>41>29QEKF{y=gg^f;YP>cxQ zbJtq$>{sgmJfj)dC_puCDE?|Eepk2=I`D&aavSH3j?d*36}@u{R))~;yrpz|2qv_p z%DH9nMU1|yB**A-Bo+M6^T_47siDE$*7gt-qhHWxcMz#o)jVqBu^sL5s06D2_HVJQ z`T%^J7QEtXa+L0&gJ^&+~xM-InI&hlfWVr*$Dg z!4?18U!t!mEBz{)2xN5lk@B#G4eqRQv+p`LXSP=b z@}%=Pb$s3(sk50$QQTW^Pa#`xx5E$XMMOm<736U(&B`h$*!|y4b*ukIN7Ma1CbdkU z3-{{a{%2zfKwI*C!lH@iU@_`2eQt8;^$CTAgBwrfyjz-i$3?_U#c?_CCmjWjbQ@kt z#Jt$U(eV_FsCl0{(U4kx;3e4w{l_x-dLBnZoS9SsuJnv_b|{@=!4@lQbK8 zx&c!3!$vRCNs<;#>svxLBbw4S5>lUh|QE3?|iekn%;f%Vigwx;kB{Wo| z+;6V~#~YnaAXp{ygCv9;(mT2NUO9F9^z;@)^99Tsq(8oL2iguwaJ#zURh$tXOiTFPC z_YDZ}3t-WZW|N6}AqYHY`LhH*wqtoVD-qj=t_23efo6y2MJ91Bl>hk}VF4r5db`)l zu_lEdDGJK<)?KZ})L*uFwbHUPua{W4KAxn~^QS#I2?{?H1G1RWt!W(Va}^3>1xrST z_s;x6&W&9y;g>iiNK#aCOn6aJR6OZQ1?t}I^56g&v13 zM}rhsoGk{e6?`^l`F`5E?(onSEetwh>%3_>E$x2$JiJhm(;wl1k$pL>3hPw9GU;vhl5DTltY*0D;??G+RCUiad2DkMlgbzz- zN(up7FbiqcbiJJpO+Y%ue_Zze>v$ER78d5@6`h#w5#o6zjTxQOT$ohVVFa4FWv%!{ z(5a@H*4)C3lB{h0w6#5t45%LI+W+#^$GNVi_C-7DF-a#ur@B;Z6O>*B&Q7Z9gni2| zEQkx2np_9US6Uj85DP0WSjAA$(a@k?9uAuOEwz}2ldAx^{5Tg%H!BhHW0=*FT$L@k zx?m<)Np3WIdw}{BupSbo8N0o6nrT=AY$#g<(0%|qfv%f$SXBfC1wqaP48C$6TN3y? zJ38qgY3Tc+!s#Wukk9A$d|x_eCx+f=w_-VPO<1;29*3C6BCM^U5hd}ET{Sypj--V3 z|NO;SIlZ&Fs>36Zk#v8er6e#DKWLe-B_t$vG2=wZ(O?+@y!QtUEsdGk*}qp!P&pVv z%_J~dx{xZ~C=t?X9Xy**aLhRxGbQ<3hWmW;tvlnpZ3 zhMbPaAA`(w^B&`^Y)y$sNP=2JB(^}(wblKA0WT^hi*KpO!^tP|wW^A(c@Uh=&!3Hr zU0e`5sCGtmtl66j5&qA59X_F&EEa3C{4=Wk_a6@>I3gVn{(FuvV8WzG z>Fw>kBVR;MXTmFU|HcP$)Ot0p2Y?0wa-3-7{6QYg@310lOG^`(w81|LoMEL}W!>20 zA9ohPTjON0`3q>j;j|iW^FBKT)BL1ND&7D4o55~!u-d+EEFY zLHD5nI?^T)pC%P^&3y2%hZ#QBo?f4Qecj}3t+xooK=2Fdm-|a~N-q+wP zWzlZ{jm+3p{I-9G?8Fy{G8n_j|7)?QzzENZ`B;-X)tDxRJe5$*X-Sf4anvf<&>f3` z0<}=Tu)KWuc$59>*DtWZ0`D&7-zup6?Lxvk+op&=<+(vOHW)W?@o-a9(@-Wj!0j%tI`3eVZZR}iKGQ{OaXd-gZ5>IdPiz;}uA8A3MdYHFCl z-4KDy>G>dqhH`!h?rcDjg^uWO*$HJGCF;9_|39C7Un5MuJ<#9ebW<6EbMo>u%qmp@ z?%kY&5Pf*(f`QghZf1s)&!8gX_wO{KH1U^(A{o2iK(;z!p;?&2Uv4f~uG!lAZ!iEn zx!_(j?##3hoAl)*UZy~AP;oOKRJaLf3M|oAQSsaG=0?03jJc2af5v_anqAFz`_(7k z4>UQ{D6lk!|6bbq8GLXfM*hGpLqiv?&t*4j{Q;v+S9huT>sMfNMf-hhcMSVGJw9F` zZ_3rp4IC$6fglea)gz2z$U2&udw=&V%`Nrn?QN{=W>R3IDKaM36Bs6C3%y2S?V%!K zwYp47HE^xXwG%0dzO9UChU9$1LvqXwXlk#T-1?+>@%y}|s_(OpL%!uQ(h1X?SEI!U z=^c1Tc!TBLj|uY&m~EoWBskD`_>VsHf1?T)(Q?BUoysNO<;=9I9;h&RO5^_EG0?~5 z{Tz{|sxJHe!3?HKmL@VPij{!@Z04;U&HKzbEZif$3OU-?5Qg;zz6JG2V5N@jTzdM5 z9OFjz&CMO(FD^DhiGOnJsQIZ5COjgci~v@@EG;bq+NNtEBaM(A)%*bKckxxqv$n3b z;`}lFcwTkl0;O`$|C^)$OBv&{=&3mp6V!=XWIea4QYW6C$iT{ z^T&)!>B0}kDK2iHe#K=@XJllEEjCxz^W)ZhhY`D3@>`kN3USlMzO-!77{!!*J`y<) z6$NF4kwI&2&YXNlA}718El7&$>fw2IHUV6QAg1XH4GqhHOT$i5R``Rt6o$8u!;t^* z4>4a~PY=l%y&*mh!_>M#hy>LotSJW~87Y;UKtW^_VJr<+T#nRyHm{*RK1hq;q+LsP z|M}qigu|#yu9O%m*gLy_`O z%$AB-4bEFVXK~|j!0T54TAx7kG%exv^n3G49Uk%y7D(VQ0~tsVg#aDAMH89Pq=Kga za!P7o(81F1Hz5J2OJ{4fgQI0{3;mPVkY?<3bRWQS&B#bO?j?Qs{)~{%8Y5Mz!{8IvJd^f)qpi%?h!ey9Sr!6Q6fC$X#yq)ztp;GuG9W zql|a0tG7@QFHjGW0Osh_pr1bz6G7^fEQ|GDmRI+=-Hu)wcLQW8Ccz$?{9f;lz8V}1 z^WS2u1io1)U)ncZn2wCZ;bl71?GBLt{{~^+x|;*7(|&b2n}GIBrwu94!r0BY0S?g( zF1>AR<5!*7_D@Sb_brR-9ll39oX>#IH@$m|c|*NhvCj5iEdT@I@%Bg*$hiGc_;c$| zXLdWC-F=HNqHh2B?})#v!7a|0?o}M7$z|8TbXx2?i+1`P2HYpXk8bYnnbf+wK*4dK zXJ+2LxF=yL`l%&dp!yF*ApH+s03|J++K{;+DJd=OF34a6iVc}+0g5?0!N}a4JrvIi zx-KZq(=4;MCqXMJEtL=Gey2gtP+BTPhThZWM@ouCa(uWqvU54j$uVSJQ&K`7Uy=tR z&=?UgLVOvjfph`w=c}rPGsEUc>7zN5#d9R?AI;4W4Ic#e#YxEv_! zXzwAz8ldUC$BL7qDMZuIlBRw4PDV|cMQJ+kGHH!p3) z)g9|yHG-Ar)C<<57&04J$7@nA%OMAJjCKLh=<#-$C)+0ZmwbTX&MN9P7tSqAfsQ6J z)LuQ&r1-v@d_g_tT62LHw@ymhpr!Azc81zegwkMA7tkr%<$hm_-d7Y*u8Ap#J3c4D zUDLOkf}x^7#ByR=zR^f7m~Tx#Zwo1WfQXL zUmVsp`{m8)oRzw8NKAW(-A_wHyW0# z6%fJ(6G2-fXkCILCEWv{L29a=-kx4Dy+((H`IVJ)DXI%kDAif#Tw53JrUv82{3nt{l_Qq?CcQEby8^-t zJ>7|BJao?VX=Lvo69NQ!f_{sMIwQn?O9fk6A)k5P;;ArRll2ZU>H`-pFL4>$!q8CB z5JBLZB}C$ukY{XR0spLf)Ux*fPl6-N5O`$ge;bL~?Tra~oEfxx;r3*S#@u9G1L>XJ z*UQD_;`d79-RV+rOn$rH%fJ?OSYF=7^}ngS455AfS0ZDz9Yj*h1vf}_W5}6 zA}ogNWTjQc(9nl785b{a*5Up_qmxmPxzyb;jqxtFSR(VfLHdgxqLE928gqkc3Rv6B?%B}AqV%E^|7$9FK8?*ESV7uJm0!~ z>EiiK@6RuGKp60-6Gj&a8QE$g^UK{jI4d$htW~d7!lZcx1fX@XP8A&Bm!0q2A zB)Bz-p@Bae4H>kxaguIw8-rGC5Pgaso8H`v%{~%WCBMEw1(My~SZ4Hufu4?l-E8hq zIKmRw0*h($g&1rmvrYT+{

4{}M>7mAj~c<$6s$X5`uG1M+%L@6i8?V2&W;zlYXHHr(zeU$Od{yV#L4;P*8c6o zJUpNI?ueGWd?XJ~16ZepP@Ny1?e6cln(dFTG@eZ!jI2wx1LuxrE7|;wP#kLQj_vZ= z(00Jr-*uWL=YE7uJo0jZ*Av_GRb_4RY@ZdbtH!)4H*QJbO5|XrO8h=P3xp!X5%(Txe8joNB5AR+#1)<`zIsI&QR)UiYZbYz8$Wr#= ze9U3dRD$Kk#npm?CcC7;(xt$eEY<{OYK8Gll^Ep;(k@7oZ>H=QlDxD{3w_*k=S9W996{}5+~!O(abj3*>z?$n^Ss0in$XPLnZmQ-(GB!UBkiaaVhH@8uqB#HM#4Qa)K) zus=vUw81WzneSDEJBu{#<3+*#WfB3BTfbBO@5PRS<-qh*kd6@gRR`}nSQWUqbC?X=zo>M(LBbBK8z;EsHzsLJ!`QqGK#z8a@)xP^)7oRC$?3OQ4(LCFqc+5a1fXmUNcyvepx z$K2lM)dIPNaqp+`44WA%5&@~xwXy|pcmO{=NTh6lVQjVOp%Zk-2T^>h4N&i|Z2TZ1 z#&df*gU_?p>ak#HOD^nn3`n;N6`y{~K=-P(czHRQnSwIy6!O9EFZ@nqeJjlx`(2DW zKm&yZe$pQmZ&>EK?Uty|F(m9%?CdUp+F3wb0J1utWs{PVdX7GU{hm)}saA7M{`5qVzm^`@dci}T7&UfKX|UYwr4aoND=!NSMJ$1<@4 zE{S-hGXJDK6OwQ`uU=b9{b7~*?2E$JnMZ=HxPWm@ZJImByq9yO;aqIQY>BX@gLzp+ z!v_tWq#&Pw+Dd_R7(H03M($iEbVb3htTGG%F>Ax22`hx0@(d4acwt&!zMY?1mQGc` z;tNI4H+OcB8W0(%=|S%I#0yOd8L=nBZ$Aa9V2Atvpp36x$-ga!MEL++R)s~gUDJ|= z8aX>K5c(l+V4yQ9JknT8Gn{tEI|6G{7lsOt2gJ0&>M#34U@;3TD?KY~aH||Bs2$w_ z??v`Pf7ngEF*Yxp&Jmx;sxh5+uCvB@||0LG zs%>V2?g%@)a}xstc$yf2=iSCyYHIC12OJ1k&Q_Kdf%f`9?2-Bmex^cTd9hyi75n2e zP{=GM52EmyZO^tpKV0s9aF>H^MtI|OT4m$z7l8;=dj)%P4R!SkPAG621`$ooy^Pg? zAvENz>fBk?4wIZ1w;OU80TB((1FW}!_==)#Tbf1w9*>OzNH&5^Eu;djo^P+do}T1c zCd9lTBn&5|w&FextO(GMD0&F9_aHab1rS+ZGuyqwLPJM>%#w1Lhue$TFV`W?o>k|` zc-{6bqfpZPJIu~LG8_0_ar85TgcPJMQEWgLFw-~x=f8-aEzK>N{9lNa1-{IWf$>sC zx;@ko^-~a&rDfqla1iL9$o7FpA@F?eLu_JshK32plg(*u6^t2#mWHQ%dw6nM^@AdW zP9`BCnNDF6cKwIJ$K$YyKy*1Fx(_1OG&Ec=K17N|_RL^Y7=9FC1!5Lpt>N9&%98A5j<9(pSGph;DK{U)#>bpnCJff9)*Mr0_ME^>COex=jhhq9)7|L z?JEO)+VTP;ep8}3HW+$f|C`k?IRi-)%6%XRf>NFQFl)AA??T;P=+!@Z&?IjC-~ID^ zK!gUGSrKns0LPy?|J*^ZprnGMSkM!BbFfFjWax2W!b}0a)vrbO_~hg*G3y=QDVko{1YPhv zEaWp)1l#2DIu&B5nES_I(|#373mmc1!Mr`Avb;wQHr#a~YB-{nJ!E-X5E9D+uJ zz_KE=p)gfMN^&y%L}>uNjucEetv1tMn-3;ZZevZ&^w`+<4|(BIIFy9? zv?&1RFib;AY6zKCS)AXud&yDz1#}wt&F8~`Ne@2{OnS>*M@fowL`UyW(j9}Jj+-+b zbIV|yT<-snng>lN)xVm4J~6vkchMprgxJ|wASQeR@C?r=)(iIZ<-y|r3vbFoyg{J= zl8jRlOdZ z)u8-%TToQhMFkv1q+HgMGer}ttsXk+>Ry)w8mz!A1=6TnGJ!*1pZ`{K{fxk->j}XI z!x_JzVDKdDSBQNmOuoAb(Xg-H!GA+IKi2$L3-ES+`}A_q>AR1HU|Hn`bWxL9X=HoK zL6G;HqYl{JJ`u)I`JVb$Qp{G5;hEPHu|_e44HAS()H{TQy@I4v0LKj0ISh@ZNd%z1 zLwkbKC>(^lxJCbxA!NeUYAPVxReq}TWVhDDPyU}3D76h+#?1ni4W|FpjXbM-l5>RX z*>ZsbU>=@A(;{mL)6*l$YZw{5fs8xI26g+j`|wu*KPF)2HDM)r%Nj1M5Kx5Wn69>a zI{^Vi*sCe{TgB8%Yg_=@82z3e@f5$0i98vP%D1vaf#P~A_5xtHJxB4+P;aK9DCy*! z8J6IFRUI7YTN-*4i}i`^dG`)@0_W!E)wK(QA48Pd`~`)$Y-K*ZUBL{h>c^ERd*jB) zZS1=g{8rTZ`MGQ>a=?|>lSsqVst01tld%oq*4?Qf)o`pk#&$#fWMnkxehj^`R`=6D zwqLcBEknv7eZgM!0~CAWr5xvb`q;mFD#E&v$i`oMX$|I~s9O(%wL7tOVGfD@*7XfS zyq-zpivQ*L;3@iqnC8dY#pR|!WOatrRc^VoP^+N~4T6>3v5U7ePjMeDPJH#OB@%lL9OEk0$ zVSj+Z9%Ig^eFOqfT81vkvAepm(FDN5mAo~5F0X!m_-U!=bpJTbwxA&L=JPP~Zc@!X z>-LE9`UIuyfKtg6Jq{K-U51usH|I>R*Ny$_JKRrQ#wz*v&UKo*u6;j=J1YcEK%&9( z)5O1xUw?45t3uZFxL2tWrmor9dE#0wSU_<=itrss(g7Z6FsWjfm;c=H>8I8eThyA` zYNADn!cII#s)Y{}n+vz)#l;`%y>5U>^Zd^;VpVk(|Y^UM=dRtEa416tBTA_ zmVb3Xjm7XgTKG-vGaH*X`2Ba{cXUIt6h(&;E8=(W*pz@Eu3cfE_DK!$pgVpRygdPE zmH;&7C_yj2r>9@k1(806BO^UM23{XwS$u0+tcX}dj4v=3_?PqsRNKDgm( zzeeN%+(bM0jHov6Q|Dn9{7@|q!ZCZn;sOk;+zyW@i!hM8PKy!osAljc1F9qPqT7;&L?8z&lAQ=s{PMuc@Q8ySFE?eI%M+arAp%FF)YI>+tXJ z12W$6YCW=_YN3dT2=Lv!O^2kZUscOl?{JoRJ!b**WjNK-&b@n;0*j$MLJ);%Rv`0Y zaljg+yGP@jMzx@dF!6*$e-EBocWyoo{lRj@o0EH!o0CcM9OuMcb7Fd`@5}nwbH|hI ztH#_>zX5xyln~pT*~sa)yvCX>Xs>d8?Ap2B!`b3iS9|oulIZAC871C4hUKhx@&aBi zKQ@NxcjUyfe;nCb*?d3P%Khx(SZ(brsA#fXCebsBAW}-E(3BVr`AKUeD*6|y7({RD z#K_;Bd>N(PyaL&a;LvgMeb#GRM@Xo92~9GD%_?~Ef12vvtk|i)RMi7%dp)K^{bt`k z2*M1ZrAN$;#wfrVL4Ekq@rm0qM~aS>J~`!|hAd4qX%&SR5Oc401~YGjf0vizNG7CG zxa4&6+RdYo^#kAOW;RZKrKU2S&H!*qf%0o@paxgQI5YkDl*ONIVHuPPFW19z52PL|Qv5bhGgAjMc}4?=3fQVlXr8 z(LPNe8N86J_x&+Ibup7Ti7Tdm-ehTLtu!ZV4oM|-bT7YHJLzyor09SIKhCcPp8{`k zs}}7iWhr+Cf&t>hDmqL?xV*PWT0zT5O5IIK0~QXtmJgf-2|6{kUqHn=0G!Mjba*GL z%{`s2d5hTsH`v|3skQE@i^qC9jn`(~s6zZ^6+3cOJFD?uCB>Uy^9N)9BTwc{$bSn; zd@3Y-S9yxsm*@C92$kDf%O4bwKYK7`AQu3eL~( zQqNc!f_@`eGgNshD)PYtFL3)Ow+`*-*fIn?!y`1q2?o&Hrxo%D|x!J#T-tk3N`Pk4(@irzNRwN;6U^?XfiJ zoM+Z;YjQjCAJP;QJf?(mK!G_2J5I3T?-*q9^ILAW9;rMWb7$tVnWnj%XO5jjCl>}X z(@N(XM6mMPYixr2XjdfMOf=o4lk%c8<+dt!uZvY5Nz|*68(TPlavhaxu?; zQ?mZ!uY>kJc!!rSgt4Q$__>!G`^dz6N2BJ8nm+dmYIUxJfvN+EVutVy4Rsu#v>i|1 zSDp?#Qi_(g-~%0Y1zr1H$O9ez@jCV7zLvVL#T!1+Fmz>gEtyGKK5 zVQ(J2-W^fN*`4>h>Rj27o0U2*j|-J%@H2(V-9>ak=cB{J7uV%E3p%*(K+i@4+9b7* zvrkYpeHb>G-|)~R204MEYI*9z--DUbb(JiBRPD>v<}c@|zf|1F=$5N;cmdMh!=B#) zP*HtQPL}Hz?ah4zs!h2*^S!?sh^}@U@4hEs|4fr8-;aM!`5temzbIHDja)|b_WB&6 zHc!9t)X(r46o)LXY>TYp6 zs;#LJq0ca4=$zaB_sxQzkyr(5pzu%n1W-%1p1;z?9~>W})cb5b!?LjhD{$Zh!sNEa z+~M5Zo(?`FN{h$XVl|^qx%FHr?zdIe_e1p+`k&=T-r*uZ690&@(e&A(eU`tEf0mZ{ zxe)WYo~;n_!Y`xAemHwpFGjyiyQXy_4?6vXk_$J0ms)K?}`HU z=*x+9w#oLnq?jbCn+rBd&797dx!(Jq!>!nxp4k#Y(6^S~uMI?G9owh{T2&W)mumff zGw;KcZn%b%W57kvF7|J_sF#QYE9U(a6AO}srKQIi%}@D6i$JT+ynew8C95ltfp)0V z{5ab#=<&*9}t@X|Uo;G+PYDcdo>ru!f}=5~*;) zofDa`kQhfs_~_`kPoX}o1kuN1*C2&BeR(k6qqWrj$65T9RzeaF7pi7^GVVVhMn2IB zi1{BG^w1)<_|Z$6_nU)Wt~H;>(gOjhjwgO>-lQp`@O%z$SK|5Dpw->CH+1rrbXgR@ zo~{o#{rozN2YDSLb>p{^D*;v35)A3flNa_C=Sn`^;vm8HMdam4>gfp#sjPMfyuL_E@{XsAvffDo0|WCn z62hi9 ztAri7RP3eVQ#+hd57H0tdoTxmECv6rqgz|J?7fBsc6Jg>4c`u@PY3hXfL95p4)t?&=ce;#DvZf;1+jVxmupNYy#BhVRVSTq( zVy_l@gfv(4hACdf>3{8OP92r!ERH;VMkLqg7_XZ^f1E!{(rLq>E$zrU`&83P0dlHXK zG>#&GVUb}(D-;&f0t>N0kt>6RrBYS#wSPN5R?d_i^1AUC!eD}J*u#6s8{|j4)gbHS zA%yH688N#cgPTB(fgG~lxGz6j{=Aa~2|3J(;&i!x}lS|ZE`hGKYt;1K4AZR&K z??{oWo?#vft&aoz2u}pve`K+VJYJa9E7JEuBDR=4-2(TYWPB*P_|&n^Z@m)NrFsQ? zjIP+rd4HANxov3;KW+;d?l|^|OlG|%=hvW+nZk);Sl+bG|7rnDEA9@yzP|Oi@MPgs z`kw3OoeFRQ;Nqq>!jYy;>Y+#IZ;u!H7@0Gr0M9@1M*VduD^Y>6Wu;atIiP}xBdz5a`v=H#W&!^_=U2|t_(B2?3#jf{%lhlEC6zcz+j)gj{L`g%Eq!km13 zA3w49oUb~NpA|ACpDO*C&yX7=ML)CV%#Lok3{$jbAXk!(bn>d4x3s}gZ=|yXuOzto zCqBPY8I7x1`}HJb!uz1<>{v>cHkmY-{`{|x@3*}vagcHxLnOp^Ev2zc4gWv3-ZCo7 zsO{R7?vU;hkVd+@yBh)NE=g$+>246|?(S}olJ0Jh79{ubjpzIJ9{Zoe;Xv-V);eR( zW73x@M=HR-H@Vitqr{M?6mHRImZ#pi4rahnJL7rJbE_09Fjfm5Fs?y9;i1-9 z(FP*|z{Zcx0}oG{~r>QBqo3=~j_vT{1485Jrua18kEC-jv`QDDmzEDB!S z{~559{Q!AM9|SZxDTN@?#%LID36@t@$|h<+kO-4>bp5Qlve|_w70o>FFF@D@q5dW={GQ=GXPNBEj0%L-!i14 z0s;lSAvSW{?DmG|va^NnyL-O#)B)WOF){JwQjrHqU+;u4EjAWbYI-^^AK%V5cvHjVtawDpiuw8VPoDv2q~5d4&>pnF(?F--*clpf=rcz48_}< zoBkoyUv5kD^C@HFMq9?w<8(JK=BD3;)Ya9ovDE<^G0=f%*u6WGK$KZ)bfuTJpV0CX)}QlLPO4(vI0-%^)=`aXCD>; z@V%^1Dgl@AuZDzccr$Ap^gymL+H$?;%TtRqLt|qtq?5KCc|P%dbHdcKYf=VU!zH_l z0oO1V2QAOK z&uNs8nAR3r&)x8s9J4k@5PK&U=>@KB<{fuWtDgT^E-c~UA8um|*)r1M$+l9r&;##o z`W6$yIsNf+Ek^}+L5VjzHf^xHdIc&~#^8K#%<=%Z1*BYRgQ5MQuZjmWqx-_Or<<4i z$vT-yudbv`on=&I}62*EXJ!m*mADsXY00jcS=*1674de%jW;u zS0F62Yej-gl`Pmo66sre1BuK2!K0?O7I5p?ShhUo@1Wm~?p>Kew}{pnV+J6E6T&^= z;NS>0`*j60x$Ai1_YcpS(83ywVZH9M6Rain^#qq;!1FT;8Fu5Mhvw(+nnHPy)aVoz ze!potH$|Mbhi0}FgK8w9r{YMGERw6H1@VZ8Okwh@9Gq^ht|A2YLt6u73zNo%`T2#o zIG-Ylgw; z%4op8r-fM>_LZRLfbG?bA?j$9BTF6Pu<_g4qdB|k~Q>7*n)1A?k6SX!^J_@ z(!T*R*o=F5CDN`#u@Y7WO%DMxp`<9vNycH~F+D~nC!am_mAhOBGDGw38zKg?)g(kQ zsq=E%Ay2jo;zcq8GfIO5r4gN-*8%Uk`x;TLim9y3)f(WXe2btJd5Gs!E|+DeqoQAI8m=jq9D zW2`lZm-0_@t+k49-nL(dflAAhP@OpfK(g0Ifq z;dX?BL|m3(Y-}QtlfczO1)qq|m!%b}47^}rZ`gmL`A`SAMMxE3Ss~7N zfng&R=eAFrh6jzWyfgwQ45zLPb8t-JCh*nmZv2+JeG8U-zp&zKhx!ioUrj{~KZpE$aTy zLzt_rqf4^bHL1||r#-alYgpiUCgMNvw)ZD(hGY;+Sy!Ya(&orbq+DleyGM5~#Y95` z%@R3IW?oaQP9V642mN6_LXH{_9CMUB!v!j+|wVu&)x!_{eEHKGetYb&6arH_J?wWjkZusQ?3XC zN#>tFvw1(ig%*|iFBbiOSsZFn= z?|EqMh;Uy-gVM?{n6sgUNr#EkB~0qbSHuzVCkyc0{|^9NB}|#pD_wUGdzgp;FAN`h zf6&E2$|oK5@ldtI6ccl26%FHU<4G)@AwH5`@91)Cb8+f`=_nZ7)v|(BTX~2UO?+(V z;;xamj3v+3!1V!0Rj{AU$B$A3H$*>1+1vsE+Ic6s4}-Lc52CV$H>B9~w1m{i-o}kmRqt1Ue7Z zMiP9AX_Htv(vHjmM60+7`VwgA_AglVv8z$yXxP)lKSAd$H*OTtpDY2-i2naVi zGq)x(`Eq<%Z*KrY#p78QI!aQn$@dv}7eHdS*W+UrkeLo#?SX?|pUHi#^|tAJwQ%ML zXfsH0F;ra%t^1WwN)`V-A%G??P9@188)IHb=8vL-G9~IiXQ2n=da0jfO`U0*9vVVwjW0zCy+cQ&PsuEcVV)Vn|~t zQxh*YPCH%H1;6c(`h2bWqklTws$6O>!i*wpcJ%ASo{UHbo9khlq_+uTMEC`9j0-f; zjvn-hw4~(VR8Ajny+fDfR`25C!RP%6o7HBgOkUStGaEm+Ju!&xAMbKiOV}*NTOGHy z6Gsv@%-VrpBEy8=;kSN^zM$=W=>2{3be^Ob-;>W;EAVM(&};cxle`sT{Xdo4szeOpmceD z@@}$U5-pnXx;b>BKK%(kA`!5jTyOR4TUz=Hw!hiFAf}KM5AO{wBO`uUMKT@%UMQ8Zpdn4l zn6Q6(M#j<6QHP8SBMnV8I~Q|Vaq-Evu~D`Ha_q?N!>xb_kLliSA{I7gC?!FXsqWz9J!`qX z+5%pjyquih<&}g4jMm@lsG<m{CF~w-fFaJ3)2#X~R zaz;T5z3D9S9uuD9=3Bs6tLsObxmv$Frgv=gZPoeW3`tkeke}Q%0Xe564L`FdQKo=3 zEENt>gJJ)33j=4F#u$!)!EH^zR4WzXEHgZz@{z+vR})q`CRT*)h?z5Mu)e;kep|AS zrY7fZgfyjSDua&?0R+M(DWt`FU==f6gZaP>%iJ1*w%h=A`O(p+pqC})ao(*p8%s1y zbEFGquQ!Ekq@_j@;^1WMMU$12+o}Q=z9UuhBVbroYcs5wNIl*E=a{-YF)pr}oS%V$ z5T?W!HbqR5eJexhT!MAZo;Py@KyTybp0l6xj0m5?QBYhzVn&RZ1|l~$HI4m^O-V|U z$ou8tO`;Yvu;(NvCwH^Bcxc&3GSi#ODQ)HjJZA+}*0q<6!4-DpC0OC|-zw-T(rqOi z8o!dg7oJabd9c7j-L{z;UZLOqk{`W72B9*ojG&<8PzBr)(ZN zG2`vw>zeD6K?h?KhZ$a7T*u?}*2%)a+??6apEK(PA(~>XDv~t$b##JO0QH?s+;wH5@t7CYWMst?omVc}rW z6!rb#e`f*J!)>4Xu<4W02zhV3iuRD$O!j{Q$w9Hf`z=B1<=hzVw+#m?S)7QyeNC-% zLwS9bkJr~WA1o&H1!LeGDys;_lzNoZvE-@eb8?5C&~$4&g0gtc5R)X6WD5YFa%*c# znS((<6Ik^+-H&~#FXEq;(a@nGFRzUqEckp+he2@%s?h8bQV{tqmF?Su&s{kWktRRk zao4z9WiKUJ2m*rB2c(#O{@{q{+uH1C;|3QiMck7sXQXUFAYP~I2@bpwy`0>ADM;A> zGn|6L!e5r`U@1~yF8O3}F9P#n;%2>J$h|3q%hI>R^6Ae#)I#l(U)@FVZQ1L0Z$Y}M zKxPR@Je7AK5honLYP^adFAbrPVeZGwUoEtt@wu3wC*PrC5Up1Fgn&XUz(mZ`52l2@Wjy8f!lnSeY*QS)8#-K7L;D0aNq^EoQU6WY$6 zjziC>Rnf$cm~h|ab}JLE1>XBw?Z1Cv5M)Liro9ijL>r=v>*s3vZLx z9TQ{cX4p3YlQ3~PlP|*bV+U4yX8g9!CS488PXuX#GE55NTN34#-*w1RYnW-N8zL2{ zZf(sQtGh6{3G09Fopz#jM#+ot-@R@`*em=LRt(zMv~vPOS+K8?;-}|!6hQJ`az2cJ z<83O7VMK)Mb$;36y4wja!BUMWG`|ic8lj|UUiJL@OkB7!=;GAW5kki8V;=24vroC z+Qq$7eM5sr)_O-7is!=bHLzUp531?u0f)m7Ge}6dzleJW)>wc;^PSx(*ICQ{ zg^)zhTbf#Jx&AT@#K5it7}RVDzfYAxEE9_V6Juk8Y0Kl~;Q$k!nh=1ZcPhXkuGnmDS z9Etd5e*Z3sOuAmAMB4D-y<$2RDXEr94>7xk^x?PLWjR_w@=pLO10W3b4K)FaQc*+* z90fRJ_mExhq#^-SW;e6V9mwk5E2d7>fctUggYj%!-E>Mhv;-b=^78U6gnz!R(!NHH27xzBzn9wJt{~@j4^9Ztr*I=T}Glgo!iLPC*sHhm2m|ErfbM2mN#o`U6)9PP98+uUr5hzOm zzt~}^b^r{-@bi|jSX}y>*YE#JwdQ(G@JxFwWcLCVKs69 zsgFtmN))@r>aA^wh>PG)rH$+gO-b>f-L12D4eSTLl~9;KR*nIKJk=7Hh03)H1!w2l zqL86OmORjw_*uh{cS67vAw`74hbzQ?&m{5&b2Ebo&DOmAv143+5kA9 zvVGsne{Xg;UvIQyd4xKR2g?V*H`Z&bDi_^4amRLU;eUqmdtP!8fW`k&;V8hAw>^@f z%9_Sq9>iftd9C*pDf5mIe=|{!V%6!$#~{oS2u9GTa_J`#g);?Z8Du8+vRbmh!0vsb zK@6>}VV2rxQsfE=TB(O`>`sYSOCD#3Z)ZiYV8SmO33wbW|MY4 z=v&qf@6k$eExlxp15!D7QQ6J12h@j;1Zm#{?x#F^)Z|Q-vl}(v|K5$8KP*sRS@(Ta2LV)i>P)DH%dw)=Onk{#Ls6%kKL(>t@B(#z z*I&ro3~mS`82ThLCy#n@a=r4p-XcMS?@f{Vv0JTrOm&MG2~QeUzsWxbE+|UXBjPC~ z_usuxEFZ`|FbTdKP78@V2f_k2vk%lmVL<`N#gxv|a`febSaQ3GQSPO!E5wG8GBD_nG z2>_FFVYQSCLn9xHE*667^+372`b4TxMJ4kW21ct&(q9E>C?|&yjf9uod+`GbaNkfYst#P4nj2!N0XmH->xFCdD3&T~O$X(`t5-rwzPx?^ZAG!!sjNqIfq zY>y8Q`=ehVV$1bQy?&(=K%{!3OGQgNvM`C(32C9c9?Jn=>UyGy9vXd#7~E^{@}dl* z+|H1^zSK40dOnRg3TOHwI_Z8OBDcC4o(^(jEFc>%4DoDfZ9LN-q4Bs(Qcmrw8_fF7 z_BQZb4Mh`+b^n3kSm62)8Wx3eyf8Ud_EH>|_j|3Y$!^81M4Z=QUB98<(%K6&oIiMM zaOx?VkH}&GmSgkMbMtgy47bXlV;N)|sfWkq#3g@yeq52_BNg=Kc0;y7TWfUqLdDtkdO!ZN0 z>F`mi2YJ>6#gfekDhwUBoa&qeh|E9OFxBFc)vTWqFY}jz^?hKes6yzmMq0eB1lnGK zJ;x9cz12|T#5&h$XYjq!_s#3S7qg-}-~DJi*OP~uN;4L3tTX=PiX8M+a5y$zbU2F` zPLarL=ZW1z?8GwtbFcs>3P8Fa&N=OogLgJ#jok0PfPgvZUYaMp>SKHTE{MS_aSE%Y zQjSj2ha)x%;RBkKS03N({_i};`_xlS4jXrAX;lVY2GvG_+T9CPh9MC^W|YZ^q9<{& zTcQTSA{6E2ZRbkoz)jra4PZV0Mkkx}Q{+88P{PTga3j2li2a|pI zivshWlXyy|6ePs_&j*TQDJFVbpuomB-3P@A9h@0R9;(mbnvVn=yatQOb&!?(cz;jR z(dBs!JA3}%dJ-52xm7O~@&_V7gxR1II-=bsg(;;G^a7SSP;h@R!}@D_Omft@Y~h1FL~whwwo;gQxQz%Abaiw9J%?lzg?;dwayf** zHDoZD=b2BYL2>euBt~EUo0kPHii!7@Nzvm@@rQHmYhxH$GSp;jxTJ0Ccx$JvU50OJ#A&}B`8?2@Z!K&Tm^LQB0*_f z?)G*2<9)ro!l?ml%BgBa@;xCGRUg|l)p}o^&(s(zUDwo=2sv;5+}>*ZI6fi#nu!pU z3{?U18&^syWTr@QtILqVum&=m3rsavKseL#fS?~LTud%Wk5=pi;JN~$H%RLVqPD18 zaz9@!X}qCu*no{9z#u|a#YuN^ywJ8(1RlAi8k06~NT|LTEn3kX)cHM|S&z1S=EEQr zQRdI)mW@-Vx{r~9mJmV+bu5-`$o$Z-swv9_4l~G>PVc2(z@gT6bTnfDvX-vFpL=sD zXmY!|{ZUS8$kBE{xHBa1>*|qUVSSw+f0zbE2ilRPLOiByiz-e6L&}(^Xqq&I&u#LF z?Ew4iy95#5*f7UT$>;E6T*t=w&$Xtbre6``5@o5w2b>XuX%a?nb%bud3ihK#J5L~O zFKn#{umZRds>AlDJ}KatcRle9nIY(iJTzr%Pe2i2c>=WAzz!|8j8+0vm2xZ?W4u{r zqITJ;H$EpiZM6w!TwL$ayR#+uI5+iublKuB@eONWK3SGN19kvw$Q?sw972 z4z7Gxd&|-xY5a>WTp(iMf#@9K;q+%Zr@4co$60}&_sHFapxmQipEpjVAc?3 zYtZCO`6KNuilB!(6H&Ker}7|6_V27?rzf|Gm(ZWPwg#%y3**1@AaV%yTdSKqI=`1! zld|?uJ;8_w9&8c@8m}WgDNJk!zL{_6f6Ebj?-E(*urV4?ND{6?1tPdF?ySY3i{Hi?oNsi6 zsCmz{fDr^OT{-Bh@wtqu-nc*rQ z{ATmLhX%yy{(dq#Dcs%#hz_=uV5Wy4#G(uC>Ui=BcoQ8Nc?wV!cEKVWfIb;$Tsxjn zq*OGS`W6ds5tsrqxGMeJ{_V`$JuYh%2ub6lzouY7!_KMY_wqDR* zRA*_5IDZ;I3xR%z((et1kfG=dNURJZm=ZPuTfIBYbvq7>Cu`r26D z%O4JDTGG)afiKf^Zm(#86uNJ-N-ou>*OxyfID5j|y}7-IL(?oQH6Y$xx6KXZ<*YjZ z$}u1%JsnZ!aHc56IU@;(aLN41Z92S6#5_%Y4Ak4t9fQXqn-lH%5)P{k%)nAPT=(`&S+iW?EE&$kQtmKL&bD|zE!vx+kO=u5hJy{J?@VMzHtaAdn#*^U~k z3#vx3KXqDW-(B)%KxU( zap7d^nE7A4P}9u64Ork@%yc}RDXb_R0~zysH8nL(1Y~-WiZm@Uv{Q)bm}Y| zmfP_KW0!oNAI5)hAWfzu%nS|8ot*-xAgJ+_kv^TTcYp?XYpB#LSg91b%Nrls(Zhq= zd#=^h29h(x(aA~&pGHPs-tBzeZsPmWz00T)V*zq-BWQc=$22Q6KGF@{l?GTq3{CgM z`A5MqLC@F$1T4tKF~b28aKkq1g_qP1-fk4xUhT>-U_d^a4oO5rEc^xxthb6dG>sdc z9&?og9Z;gw?B;}X<`%Km)?TNX5s~qxgm`%H^Vga$F*oaFb_T(GKx5^zoZ1RV=KkjJ zQ5_mv@FV`vOdr&_?W}W*cic*muBd2WQd7G+UFL^R+ci5wfMQ)FPv{$)mImWT$YEQ2 zV`&o+br`S$r=FFf^$gGqu4l_I>Sn^i8+vhZNGu;+a z!O(tVOJsRi0A*gBjZBD7mmHX%Kv%U&r7MUX0r3(Ir)Qm^csTIT_~&f2>Z9Ot9Kc{% zO>H=}{VcQ_fSO`rGtCx-5~cl6es|8cwFbu`+DmC%5)zWpb6~IY&n6Gkd7B*v6nZ{C zq)Oypa>Qr5Ri48%$0G6n<`!4W|77X@(asVsGJ%RCZ^GG-%wux>*WHwLIuEBUs%T{3 z@aYL-`l@F!T_L?k-QCH`eX95NlM3^@Bdv8I! zY>k6AT1dYousuEnjaVA@_8hFys;p7hEK{WjzLNoQv|;V8NS%;*{0t1XuP;zW^P65z zfm^vQz;q(g4eR-`BiZKj$XUFBdbSdSLV}dcEFWu!U0-74gJkksYfcdDL zuRiLRirL-Y7Yq*%Zzgb}<$v}*Kicq?6`Mavr|5HByAQg>t z2`J$L(n!Gg76ih>Ne!JWRBj~{IAtbeB$VC6XJ$T^Tppj)fo26tRtCV3!elPAT9UP+ zlC&9LJ82SW*d(U(IXE~6O7|J^9sgdL)mco2M@Pd3xPwt(KoD$~2T9q3-Ww6ImB%wK z+nI>oUWz2~{lt`%mev;&F=@uTVcZrUYiLn01RL3-*ZMKgzsV=`_Vu@paZUA1`;&LU zPJKOlpm89(NKM4xHefhGpLAwV+@y6@3gWU|Z0+53VXM?iz$|RE4CHri>qiA_WHVWLFU_*2c zm`0%Q=JVa&&6SAQ>KXghSF59}T>O;to`WMP`>G9XyA6tQ{O77XbsPacJ{}%{Grb2H zcN8*8fp$#UcUf|_L-wa4eM~krl9ZIpM+l;+MfRnXm6veEq6}wwag4B}U>0h06!v#7-45&Wny5;RVi_%nC|Znt@J=4M~DAP?R%($nQbl-*emh?x1K|| z#2>(3Jj%T*$jFGgHmv)Z&hApU=#*kB=S1h0_@+X>TFY0;0aQi|Ky=R<1`m1P-N1m} zr|>Zne$gcJqHE?zlEFI0+&nEfwdK@)2sWzq!F0K|Z-o9E_W@1>4v7pkCIZlAYRN3T z_b;M+h9X6cUL94O*bZPw(Of_hp~lcbz8cMOH9u2Y{=DbosO4pY>T^DK7mXv&t?fy1 z1_n~zS06HE!zYV9QiiI`qNy3Z1)J_p%rD)wPp=%TX0!9RF`63Qoth?IvqC)X1=~ z9xbC}X-ch11J}=GfZ_uzB6enGfGbGkt-m_cCFLLF3bWv_(Nd9E1qKyQvmPIhoBd;@ zzg)wf>tVt}EBz3(`_+7jONvtL6;Ic&w2^70l%=_^B{%0Ssixm-siJ87(KlBA>aRZAZK{CSM9?vt~s%M6fI-lF;jQ zFYICA(s~WfO*2m9+uT&nkj(R0su{Y_9+cYC*7Ie5{V6}^cg4oeUM}WH0h-3I_D@rg z_wx}Hz$DRV9vS>h?bG&;uFxUsnHktoF33R5pMA_5tXdUG)gAzeveJZhy7{SON&bHZQPW zG=9RtzZYBR85xO5CCcBmQX*_1IsnE$`bQ{-bNB&DgT3f?FfhSj=}XMvtS%xVc&J@% znenQo&|p2sdV0Df6hjOGiM(v(e}aYa<~Csb0C(T*?JW@RPMFtK8bp2I*kiW}OglBI zWxuem2D1mc;9p<|Gds&3s|{wGC3Nsnk;vqaU`T&=Ih|^zysyj+n4js~R;qsGq(^&y zh*xfJQ3yOVx=-)#Op%y{=LQ1y^~`NaF~D*MvL=actr){lYsLsjlR6B zY~V7`{vaDXz(Pmj0mceB1jWOwM+gexm+jpV5VY)%1Imatw@)W4AYMsRExL4X3`gSQ z0IOo`kJ?|XtZDVmb97Iw$)$fI3m40+>JUQSGlBMfIbNBD-~Bg#F#)h?X>g@4|A>lG z0Eal!N5qXb_pG9#gD}kZ*|md_Bp*3&7!e-ZR}O%C7zAaap@8W={jYhum5Tu6sb0|3 zWyQwgp-*YGT*hPY@$m@>8Cn8v3X#9G2uaXOKkn~u4kun;dVN$>dIpp-*n3F09hV1- zqW=5>s#heGHExHO_EoeNx5maP&3Eq{s`PaLmZ3GVtI(N~iz|fh`EtkdUb-M23Jz_1 zX9OTK)ZHg$qmx%;j|x4nHwqO2YU?5LQ(uTqsveP`Gc?n~!{0PK!i@CFr;XPa3vlc> zIQVf;K&Dd^-;{b)ll$T%CTs!Xefej*;zj{9j-_ zAxqenR+htxP92p`Kn7g8VEgxxM5s)8ad^r9e5W-K4|o{)V9C%1*KF3 zqTP@0<2wiiU;%ocYv{2F25*M;U#FR6^MV7({&yAto`;??;L66>~n#3ks6KgB6k$$ZW^Kx;)>If)7th zNM_P$cUiiMby{@mF=BC8uYbGY^>G|mW*{Y6rnh60Ef0rLpWCme7r=uy4S{t;v!%MC zC`8KJThs`BzXR0jaw$xo>a_2_it(d>Z)=C+<#wap$`%yuYOCv5*v|WI<3CrW;0xq{ z82k&k^uV!*M#w48v+c4w92bTV28LMw8plfq5S*(aSK^Q*h}LsV%(nkKb46FyYBX^~ zVqh`72F(5X#a0?!%Z{bBG=vlUd!lfu)h|IT{3ag53m75ps8RJi<^;iI_jltD$Adib zl;k8(k$C;eA0b09|C!qPNsMNlw9=$q*-+UAJnh$aRjXe;|JD}V4?_~uUq;}HNm^trOX{DQ0R zpa&p(!N?4s#ke^)QJV5@b`+c}#&My_Fsr!rpZBmWcdz#+@bhSLOR<~2 z%+WEWz#()iZD}soS#XiSui_H(xk0Ohzs|!3Vulll?>lsHEEs$NqQCdiq#4{+zd#w~ zlmfUcgIsKCW&fHKHJ-kTR>z(|D!G(RE1(}Bv;xsIP5wFh@oGP3H`w{k){r! zVt|O}H<+gv8n+0(v1cK)IlXm1-0Wd736hGc^k#*IzRBeHY(6I|0VIx5R)M9ExTUaEXK0&2n_PP-pMeoPn zVqiBkHHBhzbcD=HOu8$toO&+Xl@0sK?Lx!F)l^@Pg@IQ1d6Gl`7!SHr2Z=W`IRhX9 zN|(@(_!3f6wZSiDZH?GjI$;UZ>T(20P}ts1v|7BU)sUF2I{*B_=MB8ue4H651^H$! zm*06li;9Wm=&r4;CB*?#3fK1W9GAm7aD}hlM}N72cZU8uJ~?>~j77UL2>@5C{+vf+ zJICwYptrVjw${cK+Pb@I8cQ1MyoVv%s=$;(w$Rs9RuDR#CzNHAm7TEPyr%G}xK2QwNRzv7ZvgO?jo3$^Lwy6RZJIM(h&j zcAkuw`1=we7U*%Ca<~6wGPJq$xJRi*8mr z@;ST&0&1duL^EPZ@am>l+4f_)uWCD)LI-=~=zJWr0akV&helc9zq`+}K)8*N(JrQs zB|s^v#!_antIOvF#gTcD=AQpNT{cFxp!9d)BR+YtiXD$wNAWuCH$;~(^(1QtdXCLx z+ojrc9=_N&4HZe~8%Qns6WGI)zp~t1@Z*H%FWP8U^S`**GR!t9paadD(|_rwy#*t1 zm=O8AlE;B9j&~2px!BS^_!7Zy0`*j3A(6LId7c!i){0>ic+XxnWSisps=}jm)VC*F z0)B@WZymL@KsxQ2f4mS<-3!pq68R|D-x7_oER7K$tR5K2551jY4g$r2o%XKF${1+0 ze0_bvEm83NW<{~ydg%&m*fd*Rcj&EvIaJkH)YkSCU?XkjO855WvaPi^fy(Kr>22sP zj%5ADD)QddaZ2}nkLPV8KY4`SPwYOH1lfFLX>3T_GRk2<`2wjjvseIJavv-Vl;QUU zmzTf+^PS26%abbY-#hO=((2R+OQJovN*MosE)hD*8vQ0xcz8rbMVaCKwG?=Pp;Q&H1f~=L<78aUoVl@~RxRx}{3LPW_qBX%fb;wM=06HiGI-Une85NbDiS3e zDX}V`qN1o-Tp5#Nz`4Bl=F`*LgN1&RArd!+z%u|TXQFFO5>rma0F+;4AgSxW#nI4# zWbZN52X@gy;{qUpdSb_Ru@KV+SdHtJN{QRWDRQdy)%BTbSS)~xBYRaZmFTchv=cX^nl;;^SfWH->dk$xvB0}Tiu<380ADLh^>_=r z=l?cM+~fR5Z2C_g9%=~rpMUr-P~lR}AG|jzVd4~VV|(g!w_fj|FRg?JdJGZwQoPcE zf@zqqUVB@K*Q$zIoZ_uc&z&MXWI(Za^koO;rPAi#J3}8>3h*-fnJri;(9^X$%*R26 zxV^L8tY4Yo)j^x^9vC>E*?RFm7#YDdZEpu78S)3e#?RKXx5)3Go<1#+dn+qf83?R# zj97U5Rrh{w1>Wq_#m+TyWvD?*d*hV_nt2x3wC9lh=`p}Z+LS<&wR#S0tae#Xt2xdVc;Xh<+f4uvxKph8s za^Bwzp&?ZmRZGg7>#-=`yNQ;s@*K569WV3`zp*lchRpddP4;oDG$mJ4YoU2z*1<6MGr#Am3=>7sB@OX(W+5 zZxaV=G&|rO0+%smDCpor>jt2n{C?_(#QQms;xM+A{X-x_^yPk&o({K*8U2uLBs!TF-+(`d zohReKeyC8j^J3;&ER5z|%trs7%~HuFJ~jhvOY+~zn&vKTimu1j;gykY@mDEQ6X5%O zS84FPUIM0tUv41&-05h!%@js$P#HjPcOk!h1d$_LI=Kf{kXm|QKJulLL?wP7VBV3l zPGgg%q*yd34VN(bj+G?03!Ns-z{QpuhqZzw@yNLB+5rvwgAV>kz#>!T1BQ3NzUQOezkYJSTY6p zQs=+R%JFUH$yI0dB(5PCpu(O=wfU9~&D?#aQvK`CscHC5V=g{2|s}J-NU|mdzaY^;w_f zt&<&4w1P2+tBcFwT-^fbyAEA=^$q|3rCk3dp8J2R?m!(WAt7vRU>t8`>v-|?a>vK}Wx5(D z&0il*n5FGRjRTw9`+(#1i>If?E#$;}xelS{qnoEEK6eJ2m*(@sZBGx7V*}Zq8_1*9 zYqX_`lUZrB4_;mZpi9t107VD*OBT1EsbFBU?SzekBeF~)6y4a^cs$eA#N=8*Yd{8M zf5n5&`#z{(!-9moe!T1}iC z5b8+ZyqZL>+3lo0yMu(w4D7-aqod0UP1p4$YM1y|>7~@<&=98l@G+(Az>W+6=+3>^^c-2^zS=Z)y;mCh@I9hUX|vm!{Q&I zFVM#R7PljI-smB2{=+i(=5d0dK)z3%w2kR}`BJuHI=j8Cy*&v*_$_YD62m=xoNSdr zl%taqxsfcjG3W+??u(GubhzMROsz-(-mcJ(=j62 zrIHZ2CqOYLj1>fn3c2F*5(q=)Dj7qfp{iPIb#1O(fjn@+x4ql|6#fd|xkVU*|3`2o zt$g8pw)~IXeR1Eu)%Ptx^J}7q=NueDW@>CRXw#eTn|-|Q0#i~K@s!6Lr<#;Vr$WGo z;|J)S{c~m60hi6+iHK>j=-_*qKDO=c?z;ScHFTkf%7I!2g8w@U*g?nj)@`(e2uC$N z10B*2!7xs!k=`x;6x}}!!VASz*oYUKIbyM#G)@1}(MgU2Th!^^IT*yDPG~c9)cIwe zfL!STqyrH;F$zHXAjHMhQdZ`6JH>-6yc8wtvbT5j;dAfA@;&;Q_F7R@N_fM@VPnAIOv$ z-jY6JmOjwz!oMbopcIo83u1&7V|5QjlLpa)MVIPl9-!;8qyx+rfiEsVp|hVzs}>Ov z4&tH^6i{o?NcjiB>OT~1mf6xsAKYdx29pPkj_dJaMLhHDlG3%b*;=oOY~>gs~=l<*Ju?3BR{Hhyp}_JZ(%YywNiuX< z-!BlhHiOOAt*OlMEH&huUmu+YL~DMgUr$SST(Q4SMV+P`?I~)05lByiajyxAuu&@N z5lb5&dv#*-T7Y&h*ug#8W4ohrv$y0etAdj5{~NL)?5~bkBBfU6DH3gpQ8PKJjqdzd zWfs<7%M%bS8*`Dmj{oEBp9?X!oT>5X1}EE(W`0*aCpzGt00-3>zrrT3psv@+fO|^r zHQN<*)G=Pjg*|3}k*C_Jd;>TT{DM1Eoj)7aSjP`}x- zy;kR2Uj{~cM2GQ!CBt_2ag2cucY)J_flwbVxA|}T#a*?_Jz_i}mGVOJ?fJ>`2Gl&~ zVt=*)*zv=jOK{)bUY8vs%bQjY13fu1^L!ec#lwSZ*uS-2fFGaVOMC355%(La56-iP zK)=3?D1uOIh@MNx^{v;*QTjvb{(mX!iv9iHC=-x{U1kFUE3~Eu*uJShU;TY*c-2Gt zvJdhBY;kOWA#XgL3k?bB1Pmd7wj2;C6|;DQZFE0ulV_@^#DI~tCjNO3l*e$a?bLfv zi2NznnVwd<=U)ekO!)9AX^nXe1dV$o=<6H--3YM$}NYFva@+io5 zJ$JHzg!5BtMg?84cmoLawgO^+-mZQI#Ej0hKRMlhD4vSSA>8EU29XID4ve~$26g+p zVVEA=VAvf5SyNpd+U`S0iId2f_zURtD7w&9&Y2E!-=vV`w>m{783Yl>tCx-|KkV|1iLqw#YVPLbg z;;P)JZfuN~rexG>fR!o4iPQC5@B9qM)Mz`sJsj7kJe@$TWW7-7V|)6!cpQMitgAK% zTFT(P&(b+=kLNN#24K7AH9(hrqX#F;U2&BIx*IhzGflgIFds7wjee&s?K&TDFr;zV zEO_5vh@36t{_{$105@23TT4J*Yvm*Im$+Iyn;) z>Tj3gXvXl15LEnMC3WWj-1iSLKWMc$^*B3{z)qk*gC*)(uG`TH;0#@x;oI=gDk3QM z)hs_%M*KFxYNIF`)w;ig|P=sAu|US05x6U-|g&WE#)~au08XP|sP{ z$M&w48%{6PI)yJ@p1ssihd?}bSA0qqT9vxR`y#-gh>VJokBrjj5PK)@eO(w`hk3n2 z_9jr6%sU}I*X?aUadOrR$3{ii((kcz2Y%+m=~*JhJ`(R@le{5wWntJpTAC3AJ`8|& zdYxR?c_Zmm|LNY|eUi%EiAoY40fGE0x_)@dwtWFNmSsS)YB+Y0P`z+OlQiw}wrYK| z*VI=Ur@QNwj@OknNBwspSQhJrL%&b&m-+cNetIvA9mu+Dq4`m`yrzUxI8xL<_yo_^je$$oO(-TKsgl>2flyEoIv$fn?T zX3e_=i|D(!f7R1ME-`|@?E$y**Vt|9y5Jg47=_dE+K4jN1)<)toCU@#GT=e+S51Og ze%<1X+J54Um}?C_v_3RUc3bLdE8P=D&`?KZn!m)g)!s1gm%`X%z66Z+(# zCEbaN*rz$P4}s<_BTa60R({ox0PB>%?A;f_)tYLT@HpGah}vgl2l6!1opp*I`>aBQ zX$Xu=1rAY9m-}3pm^7*qUa)&KBg%1M_4J?G8 zl@&BvM>#LLWMaiXcVg-bR1La&z=(Z?vZ8o{lS5I+%nmxE@@A5X6!qZ7dAge1NMY51 za4s_-IOG7is|$g*k;n4V5@cn>pwTc(#`>^s@JAuHfhmAAGvY`40u&zTC|$se^))@k{bWf1t?dXyOA9Mg z&;uGG3X-u?+4MfL9Zyx85Q>3W`Z@=F)G{t2EVyit8kml)9lr@sI9zJt8emfgi@}3zD~eJ^PcuROEz8?Kn@LD$;m6Km{}p|f_w@mYM~-uAaJK$$Ur*sa7S)uTbw+#<*7`ksrJP75Q zMebPU8^4xZ^-u)9u;ZJ|mPhyJ{(4`4RpsgQ2g2Y1e06-+RPse4dvC?KkpI z^aRn5R%baq-4$=PN+$8yb(M!lPjAI|XJ3^Ns&es*P?M72KXI^kyfpQ~Q~6nX??TOW zekT9;wt3T*4pJ4*P1>Aw`2oK&k59flhwH*E_t%92woT+6f#Kphu>b;_1r(%42Hn?y z__s9O7?;&s+Oj|85v2!I&o0i0o}%tte3Wl$JUy=!uPrO^&d<#r6JM$S31VH^v5j)T z$T5r<|IvnD1JOmObB39+)qT^ntuA0vlP3fdALaX^BNW*L z*`)0`iA?BXbltT%ZQ`&D;QQCJuu86Z4I1b(gp2^&X{(q{dkyVWO@q~z5z0Y zA*hynU+xif7aCp~-_L;&_g%R;R0Rt$iGVYXf0wlw;lCK&5hJ`lBM;!h9uE5`Tlg%( z^2&X4)9StTL~UAu4=C!rbnfqLm<>8CK7IUlNT6@n?l8x3{>>qUMekLB@&u5o`*ArL zr&0a@QW8O$oC(^ta=?C5clAM#%CB&*2FL4mLal`#Ei^$N_ZTlQBh^d zty*%oDympv0#4>0Yf5+$m%ucVEih>tYU6dngyV?wm+bGO17rO5=layZEp!J*^HA{l(jlwN`|yrq;v|BLr~jaL=RjR zIK^5OGl4yi$9k%7pu~vp7vF$XTt;ix#SBm#5|lW z{QOBNgWgWf?)%UXUr^4>1ropVH+!icwJ2*V?YZ-HhnxxQ)buGurFLW&ya0u#4Q@!t z6;zaeU-)W_7hn8}bLcD*3?w!prfekIu-P%9o}-!2>s#glihq!QkV&pvwc`qjkB<Nh8$P&42&VF?Cx?cetVzAjC7|Te16zh5C|gJ&e=k?Mzj9w^%FsGa zEk$~0Ol;Z4noeCjo(VN36L-+tE=3mUY`sYB=zA(dh$GU7?i5C57-(=uEd?9v7FXVB z85!vRW$3=y6hz~C)Z|%&|7H>7UW2`ZH|{P(Eq252M(^;aaPAJ}y6!90pyxuEkgz6v zI{f@kBnbuvG|x@UrTV(#xTQWl(+!el>Df2C53w2Yrad)jXKz}~ApKvaqkS{4StRyd z;Y;O|8O_C0N}D20R6isaJe&8pmzi5wv@|BghW_$R zD~AAWn%-tZzSP@<{H8@-KK4{NlbRd_G3TjWh(LQq8aa+S3|8rCPVT*B>PRN&AGvG~ zBtJc(2a;cAyf;S*t)&gHx6hiVWo2z^Y?S@|gE=ud)6P9EE^hbZCybjUFHs0lP?D|P z@l5&C@Pr+9uFnCYE44w~Jo9s5h3(3;`;e9`3)r0AfwLwP|n@*{?oZn49mccgZ~5 zp7!phk}A++PKE-wsGB1L+%kBZAoa=w-a~$+Z|xzaQqq zUdMuKU1~g!`}Oo7v(jv}JtfTc7A_SOl~3R`giA>1b#pXdzF4pHjhD3I9`rZUM5;Qn z(~?1cs6W9IOHV|2cwqhbYQPI>1JhE1UQ0?QPRw;mYHF`PJ#;4=OuroM)n&k+t9?ME z`+0S`zJ5Lh^t@m_u)jhEDuQPZLqmvRmS{04k9#_#WyEu0 zIw)nIwr?Nm;Q9j>%%6lJ?efo`{nKJa#S4Kkq0VmB9Y`uV%FbqslnmY_E`x~(5s$Uw zSF4Q8O>FI2OC!R+-4TgrLjFssb_-)S&En$#Gt=2w@MOZOQy;SGb~yW{*}#t{w-pXw zsEGS)3y^2|-=piKR_LuawOdciqnXUuoV;mqO3XrQ`@LS0%*as%pKsF}-gQNGdjRM} zmHNPn2S$NI(BBDoeeW{nYlwNQ)q(m@U&QA@6CIcrSvSkm4eCIjOj^vh&)v+XZlP?p zB6PMy?blIs%%MaeaINm$r>!0HTb>z`#oaDewm)dzB0&7XaMO|Fsp*zZ&Hb>HMUQ`( zjg750j+}231ow^pOH#^Qj1M@zLq_v^Jf?9UIkv&oa!pk!iY0{#EKRsukO#c9R}}N2F|UBv>1Q@!;5JfNC)K4S`nJbzPlI`(-$wR5s>6?(&m#} zXT45P?{taBOBSf_+4Sh?0|z&4sK@KDjHkl4v^aQ}jZKgL((1eNgV3QkT-Kp(1pxxD zj(m||FPi4QtI{9Brk@g~LGm7&MYw@OlBHu>(D~%%Zh%TlpisJcVb1QPe137mpFkaT z4~Im|!GN|e@r1T9@nh?yZ(-th)dbyp59bf~=Ns!+tj*i%4^uI(0jHh$3TdzQ`J?sM z?7x-$R}Typryp#S?xG1BZ;R(hj*q)Hb~;@f?1CY1N#2wFwL0fLtG8v^T3K2jcJ9|V zvveHkAXI6p6;}6QKLBEV>kQsZr|p7`My*^<`5@3}{f) zOH|FS(|NX^*7)_AYi9z{QS&VQi6=fvc_R|3vib*GBTFu0TaKTf`YG#dtylila!W+hmRAL z7RdP>&t?q*VCFzcO?-9<8WtkL!VC3wEnWhJv)z!89 z6eq@;DePrELgM}7bWI`{43zGV8xk#szM{E;^d^==ubr{YG-id{sA?0s06wz|k5wpz zOrZd>y#|0-00#olm3TRbvLH_#(|Yp@Nnp{Ag4yx)xY zA&%)rE$u+UWpiW8A19b;KbqABIRkkIpp}!HnLc1s_zcV-bnox1*DPQ%Niun#&k^`j zvR&+l_QqF7GkBaf_&_Nquc)YRA~7`+H3`OcjzP%8t;yN#r68)N=Jld zpR?X(7GMGyX!3!qs(ky>69V#7NmtGuPaWU=6j{W)KJfAKN-wf_^MGN>sYSdO1S&6Q zTq4y=x*E%#f1e!|Swj1fl&sR5xeh@SnFrA^U=SCbww*SIUIb1 z9gmKnK5$)JtG$GGHj$*DBSrh%s>mtEujoH_cfaP*d^}t?g{~x9cSy`oScLZxh_L_j zfg=TFOpdDi9k(esV+hCyS&`c9Xez6^pNlXPR;lYY@1#D(HBWgg_|w5~xQQcsbI`;( z3C*~KeCluy3x9Qo9e(St+MmY~QepFaXDx9v}M)0?95fs)i~~uXoBFtVU(? za1ir++qXvb+b=Bh-wpchVhWKc!KepN*H+Q~)k0TI>?7A93B^V0;+R}_(H5bRU z0AOe(xu&pQ`3rZ}>rE_b`&9tTf&CP0_Dzmpfz+pqD0Z#{>`h~Wnz@|RRQ%e$Tq}S_ zahf9)Fb4W;pqs^GeK-$zd4$pxbUT2B=(hWdxRDLZNtdUISfGWag?lcg8lVwL z(enYcT(UfBZHG59+AHlE^E$#o)>J{$cb#MhGj)e>6ct8YBN?LC<1*xaVkUy!9L_jx z87yBG0cU!C<%sjCnjkeMXvKahA8CwqOD0$O|F!*Wu|CcSS@Gq=Oa55U3Q z4i;xJh$UWaaw*9?_>s0+v%IOxHp^+1b7-g2@K1@qP`m$Qu*&GoBiG?1q=7}d@$4`H zyX7{A{t?bNzCUYjBL$O~GO6sRtIzd8a(YU=?fk{@;!B7;1SO0A+(xh|_YLrD459^u zCjK&d5|qVcU=tQ@eXlW09H`&sa|@qC#P4Vr3cuxe1Bi5D0HjCx<{f)3$beJ$`(P7$ zpWyS^OBdXm{r&NmcQ~o>$(K6PU?c(F+oKn_wOUYZt|?JAcqU;>z#wR7%YgQ``ZbHe zFOn_}=Ay!%M=j0G;^h^(O&8?m&|G7h1`3WYtdFL5SCdMc*jT*we@y_Eq)Nzazyk~? zO*U)wN#-EYaS;*wQzb6g!^r&y26WMV!WS9iOJlz7kE733|f6@f4DPj zs_y>niDF7VIC**u>`loc|I_Wb$6n(`7FON`?dX+;03#$MVC~kd`4N)up>-+@sbOWC zd9^V6wwo;w_E^@v;_tfM@=L^!9Z%>p^i<<#EC&0-lB>@{u4=gTJng4C*JE`V)n#NE z_qitt>(lMp#LHHAc7`QiunybV+HhNV9T&U>pBAst10FB>b=lu&H)S^_3z;I>)j)X) z{E8}~B}*OWTW+JT@F95pjAa{lH)qn@6#bf+e94q%6X z%~r^Rff{JfbFn#Ck2LrXImfR1`&w^SR~uWt8<}M-AH%`~~TKzfg=9fI|xM^SwPi!D-`LrG>li zPpy9%S~2$)&{2e7_4M@I41FOmd<4*&Kiz+zHzaR{T;v0|oT&b$nLzj(e#8K)6%Gzg zRZ4M+H9#ue7b*a|UI8D0uAc6Yd7IfM*Zm2&4uS&`YhtEJTN^?5A%eS_=qg}8N@drJ zjER}FF~~_2v1+pr(sBM*4E>|0{r%VLWJ0x|r+;W&{%xuO*~Ouup^lQnp+8%< zK32c8NKQ5LOJStmdv5dDIUX2sAmk}F5EEtN1&+qP8Rst?cLq0u>}tOrL7A#Zmw2yH zZmmCE@>5L>_)9qNUn5)Fg8DJO!M;;(ZGYd2?8cUn0a!0N7JeR^X2_RHK&9ToRY=nmV8Iu{4D!g|wWkD`w;<@8*89 z@pbE%quDTT_Waeh)|jo%G8t5EDaOHt*ae172`9Ooh_}m$w0gae%^x1KG@qHY^@SKm za!y~w*KlzDC>Q#h_H>knH(>8taXKZs^qQ%ldsXi@eN|Xxn%M}7n1#O4 zI7lIH{0mN<`883_9?<$~DJ_qT;5`rUL!URl_O{+(r8T^waZt6gEY}_-ev`JXGYs8q z*c+;i4xMBsfc+Eq+_QB6o);!z8V%JiMK%j`kJXJNcgAx`c2te%?JAUMnUYKMO*j9< zkT=-$j!^5R<)jR-$X&Pobbd9kM>xuETEa;Ti!Z&sT%M6G&qWw6`9Jsm>BR>w{u$8h+I*mZ+y!I3O_Q$l#8{)$0=yeFF$>K-LSAsh?v^n(oZ&X@ zTVvB>B5=HmF2%>JcJ1E)t<8l6jfZ&XeNxqz=O<9UFSdHsT$GUm-`u~Itm*mD4zh+A zWkpSW@EHbDGc#`P)TEoM%;7iCz-2y4DwvS9^Owj~HG(msOvXwkG6sBT z3}UHX6AhwKb_R@hG@KEz!};OhmxJ94{<FxT$*wMH z5c;R=KAg@&6>oVoSB(RdaJ`OuR&+7Kq6TG?PJ}^1{N9Z%FnbqB(7J(!O$3Fr2eR0q zE=_CmU<}Sf`yp4Ie{Q~qUUsk0<+A(3j)NCi!$m9sO6p98~+ z9JO^maeqb-Dooi|5*w{ubFzs=6e;LF@HpQ;oj!gL2@o?V&ojTSd=*RYusb|Q@-3^V z?3kH+?su{tbEB`iLuu9jZW@OO(3HT##dX`CC;%QBSC~S~6hURDkHh1>$HCivCl)|_KNODxWh(upjB$=WK4{L8LleFaGj3!F% z9+b%|x{}6LC+*z6aIE;E)&Dy>TlxOzri@OtO}~t}pSq4IPqCbDZ(C7Rl~^b^NcC`^ zOCg>v9)s^oQh0}B#^BBuZl|-^1U|5dYgUXP8i##oHTK1IG~)X)bHCrIgdR=Olqs*; zKe)XnCtg@MmWZ^TcnG60vnI)h)n0L7v@zy$IIFpoq&xcTIjBJ{&!$S;xvgk?{)N^9 zrc4&W)#d5Z_Tg(XdTgVdEsfNDu{WQaR~PO7G+sdKC69@P1vXqkR@Twk85%1^zJMxT zHgB8&)UY1MF+HHYXC%Qe{XkSSTDzGh|yZ-Ttsnf+RfS6%vO zm7+roAAJrKqliBElH!u2Hz=s|GeHH(y@l<+GKZPKBtYopnA>*VpT@SMxhpH=FE0B* zrSMC>ED15p+^}456Qio9~kDfI58KZiwfzdP+zb;Gu@a%mp90Y!zd7NMiD@#3O z_96x84lywPi+&>bPFBD{anED2I~^5zPsSxtPIKbYwW``S081`Cb_CT!bqv|rJ9RqyAEJTG!b3%fZ_MQ&15HqJ}Yas)Kl&F&UPHRN<=jM4aa0W$i<<^&I}^tJh` z=>L7DxHE8YV4T$f{|VT@Ab&HkQNCcJvr{5(y0Ea2mcD>MmfA-8$FwDs2@=UJ@_uKt zaW(%~`@*r12Pp}Oi;GL0VDtXQ(N@m%!B3aVeB%h|GAR5eUAjbhE>12Z;=cqzF$J6% zATrA3@-mbP6TO!)u_Q7K;qEn2w0367=qAq(0|Sb?%Ch)Lic~e6gY7xWA%}(7%Pz53 zRvOEh-09AdsE|g+*P+~=Cfp4s1hcXed6`hlwq}3v>XKfX^s}ibPW$qY$kBAkmP_3c zR!jXeTjr)w!~XI!hfuC0eF?+0l8u0T&7l7N9*&86q{oqDkooo8;5{^S!1TOYS)`)= zu&b+kUmQCUdi|zLjPkpV28k@rVk!~20RF&(3BvcrqSER7@ChM!=D!B|s46^Ok}Ze_`ek4seD?vwqlbB&nqT42V0o}5jYbT)dZ?WTR(RCTGdCQa7M$)2t( zHM6}<{2-yP3MRZy7`xSpR(=;xF|YFe^&!D}__DpbI}2J@0B{bo4)$}o#^e2NHXyiU z^dcjnsh05w29xbYtzE8RgmIpG!kQcmLAle&m0Eu=pfFm~kC8_9@0K)@P>D~>%afv- zIYedYPUj~P5?b8AN|w%3VX*CJm#E}Zp>v4bIKQKTo9f1n2)U`RU&B}3I``r~rL8?Q z=6q#-^lPl4u-6d(r~#B-z0yR98&|XVZk#&o&}7R@uObUlwi|GNE4R`UTnKB z!RL*|1)*sqXMR7KqV0=`%O*D`>hO+pA~N5>rUBW6EoTLS&x!dVg9UN~F?G_C-kG3q zYE(AExqJ=H?q|wCQ=rraog7rn^!jH@XVwG;JE5&>v?DA;)RDI3qY2Jg6<*yDiwUzT z;jyo0MnKumV&DP$w?s{i2zUX_d2DP9Xi;I(fo8FLdKz~L2_>&lw*w^Tz53-U81pd~ zvIM<1ad<5*aa13IRa5gG7&qMOX=HqSa77lmG;=Y^rme%}Y4D;#Btv6RkK?KX@Xjou z#E4_^WoZl<(ToEPLM6|CM!E|LELv`0?Z%Qnni)Oyb2)6VsgyK9Iu;UaOOign@1t=P z%12%_7G2924@pyJ`jADod+DVt&0jiuNP|_PSQPvo3-&3%UO($SRhJW+-IRhRG>sY7 ztI$!K8q^Ym#`*1%s2!4(3BLTdT&v2C(Mqz)WjrXVL z4x@ri5#3}1_6rx3LD_T!EHpNkLb^m{7;^uNhLwZG?MfWNNZ)|Zq?Wk1g0p+A-=tQt zI5^CJ9@xkVNJN_wS{NgjA6*)@SZ>RdN`reakcRtdFnMFpFF%xD9+9M~AJRVjJUAa% zM@mI9v^6xatnVmNAHEDUPY^N#ZAg4Uo^+l-!2PQ~@2y~um@r;} zs3fEe4uTjuY}(Yp0;~v)GB}!d?-VxA?>h`TOge<+Lpd24=NEgKg@lT#ioc9*!mtMg z;NusCbjwHvDLZf%f3OzKMh`>?`JvA6DcHI-GV8q#W4@Vsi5;`si?!>mJwk&ZnFG(Fi zsF_h$S-m^=QTTPaq7L5=-kg18Q&q-<>(copeN}6C#1E{P?=F!qKQOUY73ea-V8PSz zrM*|agk+1i0c@ANjt(@!`#&-8Fppa6+wxR3!-f2;lA*BKUE3~oB$QAV=`ziNLN?dl z{Nwy0h2K6!Zb;t0CK&;tOQDj<(p~Blqd9!7w^xH+I=P7ZUE@M19m~W5Pfk>JBlV3X zZGN}|e=VLls{6#Ld0YVf#i{CQJnBSC$>1H2%8_*1T!-07{T<;q z<40~6t)KL-sSOMUb?CNC{_nd9QI{6?oA&h!w2i5ytr4ffTwI)o4tBD<)LW1zpm)T6 z3q|m*+qhabDK(otwO>~+!|G?UoGtFFc-e3#VMI}-JXNH(R5?88=pI4^KdXvm%ckXM zRWqs&;i~mY*xL{>MMubZ6UyPNQwyIGxT0Y6k~+!I2>zf}U?N5|x3t7x>FR!!E~~C- zKrn|{;8UfsJ5^X43ej&p5QYv{?|py%Rs|=uE3{O3f$23H8^#&sB2gxuZlENTf&oh+ zh6}iBxVi-e1Uf5QgqD#U8Y%OBhlPQ9lG(J{DqH4YY3}|w`eoR8(bS54i|19{jZQz7B%(h>VX7?YBMURcwLE- z*8J-7+9k2Wz)a=e!QkIY#>C(4x6^|Ye&5mS$QDMUKxO`gDN3hs!l<}bDMB4h$2cwX zW1vbjLRxf|G8jOoeSa@KL4(1Z4;`()AVa}Nz(9{rEE-ooKTXU4L$Yl`Hvqe6P1-}> z2#YrNwi0r+8byJ5v3q0ddZr<1YwkaJaB^(zjpU_dTMo7OvaO}w**Z{Pue3k)!O`~F zM--|_02jAG^5@g3ORz{32Z|bhqt1E9PPs3d7QX06O(%!j}@H zvoBSbn$^(npT6f=yiZ?{>%6V~dZFLlJ2ziGv~cC7;107wA{K}8m8YXp*{ljhRZIPU z%WYXRsrW)rioz#sVK)}#Rms{7v5f7b(ve%lYbce}=C8+hRg$;`5hCrJz3{AL;&}bBVeN9LA8V{v7hL8~V59 z_YB-m9AfkDwCZQ2S@srl9s=SJKm%%@b*crmgT(xg`5p6VMbgC(wv<>44i@yYzWjgx zKJwl4$j<-v0@Cktl z_7Nqv7O3*`UzlJ!{`<-P&%fj$7?A%te*R~lQBwM0NdDhN|8q{2K!oD>?{Uwi&$(c} zL6^OLpY#@`pkMdj_4#Q3zw3`OLtPqSx7FLW@<5DO(gAfTgkW{#rP*CwoSLk4-D6%19~Y>+OAjh1$PsBuQpZ zPp^;8qy}O}iX7QAyV*l8r*!S4v&ecIudWs(GbkE`NxB;8*kTEz>3TMQ{KBke7E>F7 z--;MUcqj}Exq=Uc5PUc#zi5R2xxK z?zBLPG*5+EX6!GQ{4C-)$L7XdhQ^6G{gFLPeesuk+i zsTA?x9==!R$PF~1{$tnUDG_nL&_yLgtgj!@sWA}T+|S6{7ZReabb<6k-;G#I^So@3 zfLXq1u*p>=HM{6lak^!aF%BCR;hUvQ235~<3CfWI!a0pU!9idi`#J{AKBtxj6;2GeERVnelRgCZL)08&JK`*^EM`RoA8ZEAZSk$%21Tpn1<`=kH zm+KeLs9;g9u=LG{PdE`p)8!cSaUt8gDlsp2$(@I5E)Kqmkls#gn5-?u5@Fk#KWk*F zlc>N6Z87$*_}3jxeYcCs2}4pbsm9;Q6aPh_@9&}I2~l18*Ya;J-}7AH-RCPW*5aY* z5n`f#W|d9pInbmk!RQswp$GTHP{W(_eZ2|F%k#)a}O34jv6% z!3F{RP^6J(GE)UXEDV)7{sGrT9Zf&$IH8>sf2@gjaKr{q88=O=%ePte!mYjcbm@WE zTvm9W_8h&dSP{NQD5^}KCt7SBZFDPFpk^ff6JU!1-n=GfeH*)>2EH5w>NdKQ|z<$ppFxz`6^m<-JZ9 zy-6nGK#Ln>WMrd|%Ts5q2yos{Oe7qoK{bO%n$9S4I`e;sLZL zOn529_g+MK?ZFgN3KTfT&Z2xw0`Cem-W)I`F94rc>(Jf z>Aqbqg7=o!f@$hRc-n{CJ74NNb~&m2n^2y9^C+vTW+Ww)sxx(vDq_d=2EYH!p87g8 zEHZQ`JoNj<2C%N%ZaC9Y8TtDMNl^v;kR-i;^(}}QeiSsnJIPBBSlp`_3!e=x3;&Nnwqq3KOams=d7c( z!=78O`iXPGQ}@tU-PZO;cK3BxH)wi)aBA^*G1AudPLy8v1(jOfuF+3Sv2Ey0t)(1i zOns1JRzsWxND}DvjbfZ~C1d#}tRtfz!zv zRzpcaA^1B7g`KmLv+4|rf0LHqJJ-J&CuiBGTIUeMC8wPj9)l@MEo3)1>bCck5a|cU z4?-*~^z^f>%*pbS)}MW!t~G&n(1AmfJSlL`I&>xru%1Dmd&O0M#kyRt#eV09Ruv{W zz75>Uvx(oc!}@Qa{*aLo!HAGjx_!!a1DI-rE=2$C2I5Vwya7$g8L-%zH#f2W#K|WmhiS#SI@el-u;6>tTf6m z)bG1_oS~u9wRLJ=84^{kE{$!dlaWlab`H{hc5G|YJ*e)b#_%#Qw9-vxgAH~z!Qa$j zdlskucfcaiyObgf`ng@62@5H0mA;{-(N@%8%iNdE2s+5*GH&h|g6t8g)W7W)Y#D^TG&TC|dk2{JFU~aV=e4-MJNCZ^qKwtKgkw8QPisS(dYU z2Wh)0^yn)r@T}brPHAgf&XEW*G6%j6ag?;^7f6sr5$E`};L9Z%3V= z+dz`$0+zF@D+*%q^8(5-4Hgm&eTu9;3%}W?o2{tIH<#(ikGkKBBjpZesr%c>m0F%% z{0go8gc?8DZ6{ z3ahrqy}b8DM9-C1XUbJ(NR|x@xPlLs&chrtZ>B@(MGS}Towi2x%t639bR|%P*xGP2 zrRnOc{s6!-YR?eKk~M5!Sy>jWHWv6l?{0pNJ3WVG+iAp-lFcjLMn+~J6H8#b1SNBYDFweK?@n;~8ocfQnC?-ky~MMWX_^6Xt#u(63ZdEJ9il(&zU z`SzCS>L6CszLFTM#Av^|uI|zCesbbB6B{h?VPPwz-1~w!~5KM_*r` zL@>hc))okX?yOX23UXft(C9Uwfec%-KbWiT3K%*Dh{JdC@)|!rK0_M;0kMeL!PMhz zR!JrCejO61ODC{DTwjM8P|FcBig;r1Sez`e|A`fGzUl)5Zve?xOxOadG>{QUij9p; zg2U}PH^!DJ6g+yqNsl3Hy#Dfxu&APA|#qIGbLYkA4lO14&-8pSC{grL?Sp5a$n!3%dj1&|cRNw7i{GV*?>?kj@QHiF2 zp5*Aq5fH!s@}3?58q%eJGsFG_+W7c}<>jS?T01V&v$cy=h2gh|C^-tv43R{4^A=#V z33R9AbN(xmK|l#z<2+po*a@>>f}VJjt?thD3K@BK)W8vh7j0~O9~)ap{FR3IEHG4d z5`c^kBr1nMgPH-XfD69BuuaZuYk5z=O*BmY4QSe&I3J8$+rqt3iMRps+T$MjWTnOe z@b$oe{bV;gpwrQIp1nOkKi_t~`8qq`S*2tuhKM&L-D>5>yjtRCWOFbbQ5jeHqS0Wl z2LMhJ4G!7B@SQv>-vnU_2T%a!FKs( zt@8fA3t=zSAF~T?G}h0^?&&SN@4*pJSvRNssSqabxRpJyO`B0L;tHtHAbGsc?bM5+2 zh;5}6`r!Q>X?^nYv#pg-liNba*R^y&V#LN^*2vd_ede=3dw11&cC83LmzuNN2fc0} z`!CliKMKW6Q~taBIgG9TQ)$IU`-s`6ccb?WzfKO8>4u*5L1Zc_Iyp@lVl=VJviWm| z^%|-$%Ss|a=J$vEip}({en;+(Hy2$w*1$1i@g8%Z5H1cGla1w+4FmBLGz??p*6Tc6 zN3u$(0%wmRnNyg@6cz$~3hN z4SZ<~xyoCXr~~@~r!_=KWtdv5rAL78Vq;3tHOJ z7qkx9)9e*%h=x@^m&c{5i(8$qY3{E|$EnFY7SvWN-;CA2Re0e1Y59hRp1$&9HCDY& z!*}8|PJ2Yg9gwwNc-o+x7 zRY#Gizdx~^Rqadr%QG<+wDTSibsI}=e19vp(u~_=f9x=JCJ2&va-eZ~dwSrAbgGsM zZ1@l-@?>sXms$aZas@EOdoF&G^9PaVAbwGDPmQ7Hpung%$Wa!LI2PN7&_Wjqy6hZ* z$04&YsbFS>+V05tIZ`$%2}681crISbg~ftG0~HCWpPhk!u&=kb1?W1JyTBvy{Ne)S zgM3Jnh0sA=cO6dc&o+>YuX(5yTGj!4OIXr5{4S|HEJsRWqLA+$M{p;H>7H`mE`4_G zt>Z2K-R&x*OG!acP_S76T#t?6dE7*X5;2UI!4me=kh$)hgAdBzyTancVW1(N8{H1>Cky{_xz_Xz-!}e6Cz~TD zAwfAGN*7sNSwS--X3Nd(`aGP?*8uDjqn>`+OhWVs35Tn3WEH-fJE&_qb9*y&%wL#r zU`LP_#r}e4n4gH#_v~!)j219-0f0B%aupr-740UeX=}HJljR(Zbz8|0ATC6YeSV%^ zyES@`qVqBG96dleMx^<+(c|SAh#G(=D5!IhFOlweuDZtkSh@H$1AiDHp9)#vESX$d zi-Q7Y61oB#=)#l88UQg*~43F8?*9;6MIZfXz)C;J>GS6`4i$g-su0*4JoV3FUpgpqYLyG@(qANySClZ z&EX_zPPrW4Dogb*wVX{%e;G%cvy1Qar-P0AZB?R|0@ zVi!zGhf^>nKVFv?tT2IGFjO%ve~M|L5%#Olz(&Eg+~UTMbjhCr=B9~JVI}rZy(45p zCW+zENCQpH)2L)4aU$Nrg~B;HT{!B#W0#-_EFxtW}vJ^_5;SRWz;dRF=Dg?;xNue$bt;Z)*HnZ(nBE!Ntx##Z@V;~fy&fp^ys z5MSS93e{PMer?~PYWv{r19dmh^*cyxWt@<~v$Kmm)+Mag{jol5v`p4!;Llg8I;YjE z1t96bMM(TycOLlpcbP2WzTM=s&1|Wsw^p;Ss5Q;wkqK>LzsJcOA%^nPb1*^&q$5&{ zO&j(kPdfH!C9n6E%i%P%gM}|f#Mn!x=2~G&o|724r`po-F^Qkp9EvsZz?l$h;Fx2!QmkU zZ#04SU56RK0Ri9K-9Z^rB7K0z{lNAGEkLO7agQlw3-;e&@7(sccbi7wN+DX+S&l<@ zRiTjzP4GBw|4dB8w)K5^8UntQfYj)uET15%r(TYX+JJzEd1|LO^`x5Z&a&ai!cP{30i?w z|1Y*JP$yGyQ~`S+AAiPA$WW<9xiUnc_?0Id^anQW#wv?ZvbB}(Ju=HaS0Q&$f#oz$ zn@X-sfvFuyw*%$nLt4bkBovHxo&6;!>$=LJFc7)#cER7@KNcO>Vh9X{KyDBHX-l~4 z+DZ=~3rwm>Di}Hx`zUpbk^MhHP{ToSCXPlQ@MtI1<}KuL;wyc)-R^!As149dGaX7| zq07LfrI4=vIEZ4nx>i?9us8};}Lyk!O@}9 zF+YC363pfZUZJwrAT zFuRD{(Ur2|rg72Wi@jmYOu;xg#Oh>&R=|z)(+(rYbKh$CNHf{kHU<)y>oqRA1O;`5 zKxNp|84U0870FFCK}|7Rpaxiz0s>yx;uL5B)&u4WnfNR`WYyhn@#o1{lGS9hg6Y;V z=(Il!-L@0YI&j-h?fni#J~M^Yg{u7wS`~(m@qs@&%zkScTpx{7v#=axZ}!f4qLB+g zD@l7E(b3a)+1FXSu1;xdm?gvMQ-T|9d$9^?o#1 zG=&$y<_Ofk9DleztmaQjOoZ{W0T}RB%DH?>O-P4krM{0l1RN+N%%1)BFgj3dN(VO2 zKckT8QbMxD0v+ng|G zYD(v7QM&U|3S*r0s@WP705DA4?YncN)tIG}!4GwkQr}6k(}i(-H9b_NA>^}LNJ{-? z!&69a{BE|;&Bnb!b2O_aO@HYEWa9n91sI)Lz6z z-Ejvi!I{}bXTDY~PJx0ulN2sR)TTc&#{TmYXm!;=pnZkDP@Ly zv!AQV4dVGUm)FztHcyP4CxS3_OSA5v_gJjS=T!fAOnDAYTh#3WmV55`!3JUM^h8f+ zp7yPv$NmKX)6VM9$|}v;K(7JpxX0;Co5Hsl^Hgv!Je_Z|0Hq@CgXUo$u=9wCcg9-& zetp8X-0Bi}b@YY6^Y*3nbo_>EpNaL@;-qN$=>HM*mO)vy-`gY|MuBAWV&xk&*GkhYvtG6%v6j^x8toZxz6zkiqW(J{NIo zM*mW8-+3hAEXZkv2-jkHEbw`fj_xPXudS*Mdv*WPCU0gV}LyN=y@*@B%W#>Nnp zl0FJwKm0!heWHO7-1mR&=UwfNJ9O>3`|Z>XC$bU!0HK-vO2lJjbJS}isgt^7rlu`m zpX=Xtgj73R9~5K~Qu=FP;OX9-F;UTu*Ph(ouqiKZ=B_g{v+@C>Bl~?>;IhABCLg!U z3YTkPd9kk_DvEcV#Q=Cr9%uj1p13~v@5KW-*wz&PiKT0EbMsf;El{{^k`k#jKNzO; zdk<`>sbnwA+KxkPPg6@m`u`*UU}0~MkBMn?6xF%1OnF(c1le;yAQLvqzKC89y1}m< zf|bJ~mm>o`zQ+D41w}=@65RV^P>e};mBUw9Avz&Xb2hU9Mkx^y)*-nhC#1hu_Y>8Y z?BSEX_0=0lkG`juMedGb+CZCVD$G{G<~Ty@gdZtO+SIQ{iL2)5@v>cS$zjzLT5_gB zeT9%;4g2()`0yRxR1+~QZDHY(D_{Pzj?A+7O>qW7#CE$kRQ!*No97uatnX1i%ymx9 z*rxc$mBrt0>R^%ym8zJl3Z%lvn40QD}4=KkXH@{iMz z(J5Sh?Bq`U-&vUg<2Y^Zu>LhPn&5ZRg;&z#5EF7a;?>^e^TVbbB>fa0XyCg$G&^Q) zb*W}|LIO9OC1SF4?d}3O1ln{#R*QVj^7@qsL*(e}EH;6=?>4`MTPDBDF%T!}gD4q` zC4M2;@}t>eQa)$**Js#hGJ(gZDatW1OiY*MCRzg3{RaeD?U*KlL*GYc0je2=;*O@gm zBNa+d^n2Hw=6O5%=h2gT{`HC2JMalOj99aW2%RdZNqsl8*A@*e`mDR+6`xSq`R{wv z&f=l8!<&w%!*8laWtKY|T&uUC9}WAxO;JG!V`wy|eAy#V7iSTvTW zHZuj6z)GwTi#{g=WbcJp0xauBQbJ;IKsNa&$UOkDle-DKFHd*9sxu1PF753^A$g$J8}ASE zx2Z#+-7LA%;85Sh9rxnR#}Z?+j9l3lvA9CAFL8t#hxfodURjK6Yxd#xzaS0%ht-{- zXmE)_SGVeEX$i6SoaM9wDJi)mO3(M;Okdcz$=`t|m*RwA%c)dzDl~~hHqO-Ce0e3w_tsxR(Ze2# z(r1+8Dw}Knj3!A?xZrm#TtGO-jvEQ?P0i{ccfw*aPfN0z6BWllK&E+OF+=oN3KVg^ z`z7jq-Y^v)w7%@g_aC_lFD@(Z=a{4GRJB zf@Io89a2*4h|~`Q47k!nh@R{;D=Vp!a$4f4a$nTTXlUQ{!(|Tz4dtoP0itrSzeG4k zEX@>a_T&&-)n}@Z1A@?P9P#5@U}Fu3&(`@`DGae#FH;C?SG{lEBMvWH1m`!Tpi9u{ z>Y6c|kM_J#4a?a`i0EXEj*09aSp;z|tiX9L(|s`gv!jPLxVK%1PtbCVszh@3*Hl@G zi(WEOBM0?gBQ;x9K(ObZn8*>7fNuyDT%68+ABH*-F0GYCSl-=|!Sg!km8I>fA8L;u zhKyTi9BdP0m{ta0UDo<(()k?T?p-h|wPed%DI^t+Manny*6-11?FA)7zvJV39G=b> zgF_F!)0LIj>3Zgu4G?lWy|U+@`$axMTA&&WbZ-?ZEx(l*P&Wdx0=JGDFMAnX7D{wLd) z2>2766EF0CBOxl1#bbRf@%Y9l!sDy|ZSN|+$eUlM>C;9CakI{u=#fjMojH7ttns?Y(A0k(SC#9RRX@A2CGdv^ERap zXbvXqI<#_u>9BvxZ(*lBr-A;@X z)>2@7B9rfQx!&#ZhLGg3-Q|FT{fWb;8_WeJUpIb6MB7i-%^&1T@L0X4xO>iy?wInR&+Dg@I^a z#-H z0FBX?@}MvV#U6OIg`=Y^Kf%s6EL$8@RBMYC>CT(Tw~i~xOujp&}$1aaA<8GA9S zD_y$&oEVX68N`<%)N zF6?aIGm$i@BIg*yaz0t|qB8qIT*hDgL9xzC2vRdN*qnnn3@Y{oyHxyZifD;9qxFdi zzn5z=;=e`j$T)bfx1b8`wFG~~C8!#}0SwxZ1W9F*#EJL#SPu-)Y&8%J*asTl;?-PU zYW9t6HHVBQ2mBywMm|q^s++Ibr&8>Z2JqlBKeCrdpk5;o!NbE_)DWUd$l8OXOyTbR z2Z|(SuhBq>jV~L?EQwu;+v8}={pRxNy_qd`FQL(^jk=BDS!@7SD#r~CxtpKRZ+H53 zt>wk;?x(GYd=zzfi>V)mmTke67Lbja_ugu|@2$7WR=ZJ07{D0d?Vy|S(WS()-MM*^y%#(i-^LBctZJ}If}e!_2%dpUrJsWua@h!trH7FzCCjrsEy?wI>-!7j%)dMjetdrXaV7U?M??68R)V5+v!2JTE z62>1XCt(r}m5AGkW{rYTqbfXF5h8;nJK%d8We!gKHeBt-H zTg&u1Ykha_yXhq(GQA+-obU1Nn_P4N4~sdz>ib<(VqU6$gS1xrk%ZP@|JLF#9gMi6 zs;UYwa+W5&!Jnkx_KxQP*wF7&iZdEZy<`zfuUaI<^qbYe`B(T>Z@;9uxjD#+0gE)s zZ74VyZGHXc7VFRLt}GXeE|)X3=;+ak|MLQcA1Wv??ahIxbyrAlv${bsV@vhLMNUoll{tY;ee}j zSYl#gl>8X>JB7dyRO9>0{GqQCS+|Gt$T}iEx7c!F3qOB4AooI>&+W!EMON7%uxGj^#n@mTC_&D2|o3z2-Bt0Z$Us(G2Z2{zho_BPrm+c$|t zp1PS`RRTIXIXhyUslu5@Q3HD1d)Md~CE9${DW&G)Esc-6 zWM!# zjY;Nny+8g^#6(qs%ulS%z{Jx|P(cbSK0qWGEn4K@K@s%eO}2n9RcvB~*CoBA2W-Zv zkFA!bhIDQ)&ZAGf%5YMpsXesjrQByot^E?k$ z2!SQrZ|^KU=FpUoR$NeCBAPBUW|Dm&Mm4UmjDBj*lwO>+VAM)bEBb+!hA2w3pRZaP zV>`Qsi6DE7)$>_rP)Nablt8Jw!0oFvxqa-~IDzv=_cGNHZ&yq1%uxA(NygHouS&RP zwO^cFJ#OEGth!0;Tp50v%b;!|5%5Q>LnFQ>_R#3oIBZX3rl7E}(6UCQQ@_aFYks~^x#*73u~#uy+(UC&krR;)+2Dg z@W*DluX-j~UyTt-iaH9K3j}n_10cH2lY_|if!LPf2TV7ihsQ&^>#4-hQbtq~Pdaau zm`^B`3nI==>TK~l#Z*4W8Pi=3i>aOvWV{>`KviqB)?|B(J%joIyCB3L2x31($>`)# zFmMSQEwAzJV=i8I>Eml1KweXw`7Of~+bn-^{)dzG`qi14xL{^mQ99(PA5P!?t`sZU zPmx`S2%?kmOT9YHy(jp>n8a$(X@0u}BE!A{w{?n!kKYcWNVp%@S9uFq4`e{lNPAAr z7O+=yJKqpf<^vIt>$$BA3>9Xw$-)iE)mo6oh9DaO1eJ^Ta#1ORATDVsx&PAgzRTSM zuctb&F_^b)+9Dk2Fz8+xS^B$BC9)St)QWS&NQZ~utK^^aIK_f6Tci+z75r8JS~a`X zCiSIvqu3%7Hz&*2z`F_L;{~Jt@&+dGnk)t%T^_j*gys?%?iRr6#26o6YywPby0Wr= zv;FpgJv%b0a#9R2s$3lm&2I7T(~Ly}SbJsxyPw0FT$g%L35orgfe2s`q&UzPL3s)c z>P0xy&`9||0I!{4EpQk4njEQ{Hq5~R6zOGQCPx>c2CtS7$R~$ccKSYE*_+3HMeU15 z7n#94_Lb{d#z!z=Q6q9=Agbfohb{#4Tm5h%0zzLWshbpc4$wOJuyMX zb4+YX7_gy$Ah<^+v<(#<(wzw(7nj?*2wviwqa$LXgxC|M$n8MUtyAk3>TzWP&PPT@ zU7{d!TQk}SA%U2dO8uwZ?rdXp7>eX`ZAfI%mS>jH&p{&xiz$|zJ5EbTC@b=BOSPnx zHc`72O>ZOiCk86_>8icSMG>hZ6h^e&5lOTLGmQtorV3G>ag6ONC|t{806(s?4# z$ZzkZkVCg~(kPn91FHyZR=fa6)K*K)Hk9*QQ_tugFfs@;i+ zC@rOb#?4FA)6>`Q9VTSkzZhHiKPp?lCACc~30oV}?VQNasuz7t{RFwFj>sb)|U%$+e z9$9<)?fO9piMLlTY6b=dR#ruI_4!}FKC=nInC`|P z6T;lySkl1I5P0wj(o?>#pVYe91JJ5X^7H4zzkl-w;)M~^)zo@UKgU7qxIH!5g1|31 z1qI-H7^b`UubIzwrJ>ej2r$!*d9C5LbMVWfWO3Y=YJ6WRQaNWiIe;}b(5x3P%#I+w zeF0Xgpm8;v5*`k8`N8LWND&wFIEwkYZ9b8fh(V5yDI771LM~WQ)fYCLBcG1xkYE3G z!qm)6gudUW*Ys$%-3?fTJj2c$-8|Z5W@ii898Q6>7SM|XSb3iRmF;^Ar9F^tejgr)uz{nTF}K19vFxR znVM5sNZZk$>R>Ehhi{uH-j|5-{$cFD@JJ*_VJThhAB0lqZ%UKAp9TxQec0n?g!Zr` z(yk%Jd``^BFiBL`B1~5>cd0;ziIX^}tX`06HKt6Ty~^6<5$9trLi_vW)vxhm+emxNT=K)l-xN&K#6LP$Q&BGD~0w0qrb1Bl}bg9()kr5+eG-D2~ zt%Nno*t8Rc)oBwY-d$QVlr!e0vK7>Z$w^xxet)hDN8(FFQiLGmX>27TvmU>>2`0CW z68oZHz8?&3=nt9EaGGIehD4G6-}MCryH}$!!y{=#f?{cu)MBjk^k!_PANFvkVz#^F zikvJF!zK?jG#xMheO0k|LPrY8Fi)a;t*di#S9;IDS(Njr3MWmDw|c+G{PffEsX3ml(c%n)?)kGe&XWnQbvT!%0?{P zh|`MW(s5SjV7$;LCnryra%q2Do>4(d9_oJup0-Mk-CjYBGs!|ct4^a`7uAn2_JP_o zF10mm-EQZ0fK}@1+60{VJ=LQQ&(6p&F@J85k7o0=cE>fj`Ua)HRSMH4J4g9Pv3b%nQtIv5{4qcwA_vy{0oHS4A5U6w+xP6b3 z9BCg830~UQiUw78;-Z_QLy9peiC9N!I=4v`wlck79V52X|A(#->8@To80Ma7O6k z$0PB@zza#M%R5zT(v%+S*bEn0N`uQ!5@xFQl)2j|jVnk2?`&UPHLra8+S(bTV3?SqZ5%~L)C6kXIPFqyNL7Qiu)b=E=PyC5yM?vyt49=Km|t7 z3gP073>(KMsV5}Xx|E5e%^Wc>E&M|^#E6LxbJ>fR>SedI&*vy0bb4f{TViA&O8lZ4 zpPfC)!lG=(4m_@~u;OucYgw!Xn|dTEL*ypLCacRT3Z{=P^ffdfM^%*}~ISb z6_bmC-di<5gtnyK*koqLyu1zzn5g8YGW-y15Rz7CEo%9!pwJZDKQcZJEhNEZ#ok_0 zQ^UqBke|_?D;jK}<>w%-jyufB(dEMxB^nj}Ggq|~Lz)&=e0V$Wk8#Wpl9N+T480l= z&4n9&kFch0Vte^1uilSpd!~l`)EQ8|vF2EzWVG>%&*-LdrE#k=taO1 zJhOiK%z49$9`1HJaNqS;FyJTbDPLOznikE@y0e0iJb4GbRJ^pkNfA!+kJDfGoA-Rv zPehiZ$K_)-?Xd{QZqE3&yq|CpvSpH-8gV%<{lNtRpvHFyTXtvb>gwuKgR+1fpZ`kw zDOL&&Wmff_;_`~`Vnk$7-^|QRzGgLG5cR{$UxDUzwx!l2o$ref8D7LK!KP|kDfLwt zMM%`7eWZ8g_Tt3@KT_gjrPzeK-WmGb?|434%`10XIc|Ctl}Z%u;T*hzQVI?^`5HGl zy1^y4Q`4QdeP$EBc=S!J73okoelm2oZPoV)ZyiS84E&@3E*~q!EboGon8<;|7|G4% zQVZ87}3n#Sb*>Oj|6Dy7=fYM$3`*fu=FWh*Y_u87R z!@&?<$aG^Rhe|1yfQ!2g5C>{^^t{qs#E^=@LPT~E=!7XB<_qrQ=?`B~QC(Qb*x}9~ z#Z_X;{eBBuU^Hvut@6Jlyd9cU`7sfnMit~Utgon*h6SZ9d33|TY))2NXD<^lbgXYD zWawupD;ec67w0}d&^EVLWt_7VFGbeX$&isC!@Y}knI~KjVWcfylake_G29>B9=v`e z+)Y+HD);l9H7Oa9`Y_JErIn3)wX(z2*OZAr6c{4?t>K$Re83*tps*9199OGEfDJ_+24$&gPs^I z-_yG^zs)Ff**WvI@(|s4N;-U((|PDNH;;!FO`d9l5sEj^gowYuG|N##Lh`vy?g|uP+&e0Dv;*ZpyFL1EBaBRWW0T%}c%tFFJxtYE`-rnH$eXeqG zb+p{*cql^sTVGd)b1B$=b>#%46I>ny>55}JMo*(otrlPSKGZGSc*_-up9}i9>)EZ} zKleVfcjmyrzzC%p=s@r?6SE8*)RZ?x+E-Olng7B2kFo-;05%ILTh!9f-NT9O4cYSz z*Ofj0-x*d8tbp3``*$$3hlfW@MNWVSM0uGpu^bLAuA}2ALkP2h9_kQiMhQPS1=oggAeoB<;f<_$Oex(SsqXlHsj&xm|D&{@>** zcF08?zT?|y&Z;Asd0CYsEvvchlpJyH1$fQ}$HzptxMt?&MWv;LV9%GHj(kOc>I#Q$ zGW7HI=4K7_!GPTA?BW9M$%TGEA-HK<#o^&d_tjNZMbj2=aBx5sE1#(Y1qBtji6{zz z{ol6%u4gvkA*;Qw=xg;(|Kj1h%*1KRNr-u>Pkw64eit}pX~1LCM4hh z5ZJ_I=bd5}I8_9Hz!U=|!fb_heM1A++|X*CI5Jk3Pjhv(y`?36{D_*SX0b}C0}_7_ zL=~reioY<*uN+4xC>TnA#Q*nsU%&X~FqD**<|>ztGHJw~k44O@4|G^@=%Zy&Q&YqG z78DjXHa3FO0Tw!a+Wdr;mezcKpNS6qV0Lnn6*$@O3;P3i;qdTqZ$WY#d!X*HvtuDa zPGY5tAEBt^o}06}7h=GK@nZ^xpra7RfJ*uAjP>{5Ixkc%gZS&Tw|MHF7#F9h^ub|E zSi|+F!?23zU(Rc5woJKLQDTk-xWRYxG14k4ZEb^tg9og7zeh%TdEp0HaoaGPrj;w7 z4ys-L}M?62T z(cgau*;Lo`NC)!QKmckWR?%E1((D+w3$Kb5f_NdWjY45vU}W4$bqkJ#xH6==PUvWi z?eX_#o`i%1t|~h4rFs1SpQf6Qojq0}|I(eGC3y@Ub##YYhxe5PW*Ykk&4NJWIZvc( z2QG;>V+`^oq4)!>S(==O4=OGDOq+D+_H{(upTfkHx}~$BsLwS5=h?h$S}|PV7@+!q z8bdSFY>TBJd?6`{0MzaOU4h-neJbAQxlY^i&h+v)$#B%-RP*^OF%)>e5$x9H3^_tf z%UG=+eFC1zb7KKyP{6Q3_L3uXhpLV5 z$<7k}JARuC9;yyN&y?96QG`_&AHEZ+iU`>n@?Y@Co3@b04dAJ?NjqNn-@TQG0*}zE zN=rxDe*7kiWp%5W_w`^%Osqj_mVuFdo)qO9|7=%FByr{-`+TfwQv95|qLB2eJ9n>_ zthJ9z=y0iKF6@q580kgB;j)grD1v-k+aPA*;P$n(4p)5giS6FQ5@U5;-p|ITF>VLw zawRbfDcVb<;Y`Q(SG#)U6U^G19_ z>#N-^zJ_rIwe|lW7BE2mUWMkI`DYZu+q-(p>wN$*e+~}gb@}|iSK$>80aETl^I8DA z%px)%r9KUHS8w0G!J{p+rbbe>Ko<&I%)l#6vy{@9dQTsv!$Tg zdtJP!OZC{RN&7NkmFYoSQ-3$T=Q=yPLJ3df(9fbgI@dLj4?X<$j^e)y1Nr}6Kwn}# z9kv%&A92HMgQ=^7&!OPmz^7znEbmfHOG^{tqywjv;D7HXii>USz}IM|ah z_NtWyIHawvCs!3`qM?4}m!+M#VYQzjS!)ZIr_bWv5qI{;C zRPX#^>SPdd!d}VxiJE3KR}z0rOIJbWjZFJde5Y~CW!I;X+>t^S<^)YNPT#F;Qq+C>7*Dkwe8p+2qf(tVn~pm#*jIah(8?+dF_GE0ANWk{`15}< z2}xK(BC9SMD)Yzw##G%of~ET<@Ovi`bMPO3Ey9v9CL+^a^WwlYN{y#;x_1X^@5fZV zK9#W&8VBdGKS>pYQ&om!J;zFx5(#yp&JlRXPSA(# zqeP<(?aQQB(J_&m!xSP$XMUUHwCkQ!7^@B*npjb$`+0Bjb-L7op1!p5{`u!4nLMz1 z!vC`n$@TH|!#r;q^E)xZ8HMX?NVr2+RLiTqy*+ULU-)hV0?Fyz;K=f_$Nhy74pL!W zp7B)zJ#{F0bX0`b#TI0k>O!vA6G*RRBUp+=VM+G7n4+||wQdQs?i`*PUs^O(LAswacUs)Y*4Z_2j*j3Rf<}A4VZUh#H zUI?f-iP%8z4Tq1uJ6fvmLyT4fzeSW$tlrL<3O@)TkHUY1e?VN|ms%Tn>f2^O_@yLv zr|ONkhRNhb)VOgKIGq1oex1CkNUnJy89o5_1Z?U!k<0nLjsqf( z+Rw1n-bF81QCM%sK@tl}lVR&_(o#fGv@aO^gcqum!_9T3$jXu3eg{}ZX{R1+eSV6% zF^7wrhJ;l;Il`esW=R1|=-(N;IG)FQYFGPcrKc~((dt>$l|I$6WiCHKkPl8R>^bIKyo0>ky>nZ zy2V8`5R_!0ui)UsX+hD>ET_R@`pXGd?(3#YU~7Eb{_s76;eF{@#~gXx@Tf3ZVB1dm zHABVL^56m$k(Lcz1CRRXaP^04CX;DlY9d^(LRb#6JfkYcWK}i9Xq@j19|8kA-zf3P zK7sWBg?@h%%+r~xie}!^XS~=;d?PF}`G)~@jIE2x-qEW$7M==CN?7>w;uMRqI~Nsu z!}!e(k1-I|ru*qbBnkV>(9qZAt33*zck*e&dzZj=6P4VT9gGy{{%&b~aI>5XkovGG6rb6~x7VucCoUDhNW*SqRp7pirDyB+ZV*WaW;0vw2$_ ztHS#$^4>?E+g`TJ6;*r>=tQW#lM|sOJB$G{z137fig1|MPpdUQCmIS=3OJA%^zrU( zTeB#r{PDAY2G|D(X83Sjbs4Z&2uX!F953koYRmkm&}d;6_)@PRfN{p?j9)9IKy>(f zo?6bzO6nBhDE;l7WFPKjpj4Qw!)aIELdB>ewwT*I`(@ zwsX6ytW{8-X)`OdDU^EvQq6h%=4SA~&R?j8A^5&oS-&9}g(nzBv<*gGDpUdz@`PA@ zu3zL-UW@V`rZFi+-3}am%O+*i|Y>Ee=6yMH4+lP@C*>%U> z^K@Xye-6nZtFVMs$HdW$R)xduM&dVBa(lKkmSZo7`jt$ z1#ny*O5M=#C`+AQ`1@a1=J1%AprN6q3w|6j4X=Dv%(k?)1}4kNuBXsYP(2{P6=|+o zvib{nSz_Yi0-EwNsC6} zZDljgcD@!C7GSZ{1%igVae8yN!+vHGeRNg|bF~dw7lG@CCo$;u>2_Q_J5r@v(t4{I zloA$xT!(cqwX(!*b|uBEdHn6NH=XW;*;5xOvy);qM)fzzUpV-vGc90VmM*?8PPP}q z2RA&bXZuQ!RhhF?_jGkgP~rG3BN9!42@?_WGb|!++O(pN3!K(-?U5Nbcea?YIb?>F zZP_95K-K<238O6NxFXkak(x?nk>05CPD}rRX|6MnIuYWr1 z*ZweNEFF}J+*g$L^if$mB+)|7UYwhi5_y$)YFUpEh7Ey|3q{?ZPcY7PVp?hnMM}+& z+c++srwG|RLM)WPdQ_BTYS@S`(WYQzn4zal_cRh$46V&S>&N(e*%A8g7s0R_vmYpy z#q{n1%J_(T+=J@Gq)S95O`4(_tz}rGWSB~>fd~B%_^22?B8UT05R7c^rCy-W=wvAT zdX5n@zRd(;z7stM|VLB%4$kO zJ}x#cB>%6i)%nm6o|d})cz?bUR;FM3-hp$F%0%(K(v#J@mt9&Wmea!hZRkBLJX%nj zlC|y2<)L)RqICXbUiiu-Oo}0fU$^*9%PXNT8E|GN`#)LbW}E0Y-l~}~-R@C4kP7C- z1f3))0h;u(b*fUjN)n(FRGkX}m8E>6G%J7vSO3(EtATw8Y##kHDiLQ;MYP z)xv2}9JgOh5=~V0SN=*59XT$cNTCXVMP_DaMKb(l)o%|jf4@B5Lf<08Zw*Z>+})-x zYF!z02inK5tK36R_w|wdx){@B?WQ+*+jRE=JP!d~1qC!yw$HC&!AE<0E>CyNtkv(Z zo=;Yr7v~xtkUFxTHTQc0puxmX@U(?WbSG<34`dzMJPZ;8$hS5!)C%u7}0sZA$?;Y%m2Jec0=+(h}1BWsUF<DHYK%&cNJPR@8pBDiP<>>{c#dh)-hby{hWFB%&WSA&hw&L~) z!dh?~8D=eX%E^e`TwwhJe8A|kf%`y zqknm7Jx&7 zbg3k-Imq9vbhnKfOoH zTR9bR@UD|3@gUFUPfgC>0#= zf*}d=1ApSX5^5TbBO6Mof+i}YGk_w%zW0s|S3Ucac`Pgws~BwKg@t~3Lq~WrC9K=C z)3eFaj%x^yxAjy--{?TG$^U3Dc?o@Ed$T9e*k4Sc@Sc9}j zCYTdI%YoG;qc6onZJ}2Bl7&Y+n9_yF>V3ohZ+fVs^r>Iy#1V@pQ+!G}Ha3RKN`OkH z2l|a9hDrnO3hW?+$C%5vT3N2~?hDR?6UK&M4JkFMvon{+T@0M(>S(F>g_fiyo^Y= z+ji&g%AaJ-sO6Ul!>GQ%;CsJtD7Ctl0Va_U;xDeq%vxhzklQn~!TEfTB66~En26#K5r14rWoZQ!rYiJ~a*2FebKNva|YtdnfO5i^R1$irw z)GpjE1pBw5^!Wmhm~>wIu5SlFzeAuQBYR!_vi|c+T1H0Z$B!Q%AI913{HKHrNSfuQkB1Txt1 zBDmcz44qGbV~vfC4F?AY*!cibFrdErT=s=1iioH&Gy(-Z2L?yD30K47Nz$-oI zSXWfz0s5^Yx|X(xFY+#Fl(aHMoaF3mm4545TPXlfwJj*I&Oq*Rw&!^%@=AjaY5hdI zBy`g|TldR|brIdS-cQ!E1SZ`UO_i}w{BjhY={&YqmzS1{3eE*VOZ)*RSHOnJyUTG$ zLP6mtP-p{YPr-4+?%dqm!DKc(tPck}yXE?d@)g^yL6USlU2YHvA%}&5**!Smwe{|j zaj+oB9>OPl&?Da(|JN_sQgD{0^~v?D!|UG{QEw8Wip$-jS?zjrbYec|)uzhf{!#Vv zob?bG_n zexx<|dy2VtUAOdcsBVXI?B{qLOoZ)$={1YUjlpXKih3m#v^k~Auxr(Ze&s8`h8Eyuq| zjAPq2)4hCK>o^iBv46+(-U*gT{Ss3WMNC7+PoJP?*mJd`U%{T&)5Y7BlrlynO4QL9 zk)0@=(oDvDo`3v|mz$_5Lg~-Z+k9E@;cW*w&tiaW2h1$nJ6%`QL}^RHs!stzJT4xu z5YSwE=yHDBkeEH`HKJbpf`&go7ddgIIvbhbGn=6-)Xe({gkw-Nkgg!YDvLP&RW!Y;uE1p2E7n>k!vom3Yve^0whX`CoCB6-#@Lz~8tK zusm!JFM*Wq(K2C!rkj=W`>Kf1-3W9#v{PBtfYj1&s0jynX9BxF*~pa zdRVg{4;wok@N%ab%dNGv?oU_UC`>VJ5i`0SAiDtD)CUtdSghs*P!dsG2+#N@?OZ>^ z)5)VCB9elvN@e9~;DplG-;d9%gF{Akc{pu?(X)ju8Y(O-#F_frda)|tCX+4qcYlBX z=;-K7-N0<|WYw2WxHw_Z)na|}d-c}4Z-nbb|MUDim6HSwKF{an5w&IK&p?E@F+v=c zDGeXbbRW^!N&Yk^{|$*8PLIRYNhvb916YTX)zAma4DLI_{7TgGOzpG{l-A`ee8#fT z`Ya;t;(>dvPvdpfB}J-!#50at-8n9J_5Nj9;*)JsgPTL|*pw()b^(**-uzsKcN%+L zJe`yUSn&x62*g}cLA7-82VniT)ZFe519WF1EG+MX3DOIG=R>7!>s}!m_;B1S_)%+c z3P}DWfkb@ioG&@@Cpx@-^|4tySWF0&kidH8#f8ic1i|SF}BccE@jifk1{P$}h+W3$+X| z+am8TQYsj1v+6ex)Oxc)!zZgqHZZRuvO|fsbO_#Y)OYD|B5I@#7EMY!nvPWM{p2nL zi;`0+aML4J&7DGdrRWzFU3&t$(Jk$Yd@5+oNm$IA)~(-Vh({yo7r_ zpKy3ZTav0-o-JRw0QAjNM(aV@lAVDa%1OmMi?KmSa2zgH==vp2O@rZIv%mVkfytQS zbMKe)?Hr`gqvD(#P|SXkaeIO|dMah{{wMpcF`l`YQ^SE3DVdhNc$0ZKO8R2&9d(<^ zEfQCCbCJTWB4?syJ_V|w3zH(I zI6k)VPDEDzyZ5NZ zhs5I>zSNi`JN2XCAJ*Ek5=~1f{sZvg2q|(qj{Bul@fv!I@Baui$FH5Ji($)B%v+7# zR5is7y1O-)Tb4_SX$YgWua*nb^IuHbuIv4vSNv-_uH(h-+n2>QndP~{z#&Oq82I*j zFB=Y&@%N7pp55+{Nf{0nSS*$Z7pBlrLVPaFFe7@BY1M-h@ffS%ue^IF>2&=1 z?S$Z%mM;&FKsOZCw-ZN(&-Y-t5M@CmueJBtTmv~fZB@~4BDN%eSZMqx-#}wPOm?qz z+0Q6oJ{j?l2|=MG1M;3k_IrvHm(EtZ95X0e+FD1ZMc%XUvln8YyxHX*%cJRP=N5NE zUOw;{k68$~-w*%(&BQF=16Iy79Zy{E-bprPb!veuOHaFvI*)^@%#{@#AnX3*d6Kf5 z@@#5lWd($XNRO(@%C&$7m%}fzclUQuaVyv3dLNK3$c21iAWseskXh_EI<$eQVM|YK zX6AYbGQRMOp!)>`S)`#i(2;sCz|}1*Dnjy%hHou-62})p8-^#9?K6KIzHw9|o|YC15J8n@NGw!6 ztFAQ~ne7?1;{F%pCs!+JJ?}yt)y9OJF|S)4&;4y^d^aidN)yj04lc4GAYD`}UZ(Lf zmp8|kH=6tGFU{l3)bEOqhlH4t_UHJ<*D#s-AUyTEE=%&K9KSbz|7a>G5MnH&th~L_ z_%}x7UJ{xc7w6{44X3A1RzNY8W4=$+`A3&Q6j%ps2&@sGZT9`Xxtu+4SdF<+GX%KmQL!lBjSC>-*jlG`{Tb%7!3HDE8AzD z4#|r~A)KDP(2$X*YTE@rMG5B4lGD3CPdUVR@OO8Md}e?jcZL3Bf6jF+{?0@=rs1vn zOG9tOGJ_GZt3psPU-IM?(P;8q@@vQ zkdpk@?EUSNp9AM$oOfn?pJ%Q6zEXlk@R}WW1Wq-GcTnD9RyLUje^15^T_SzvPc>T) z9+Orh1_ffiE`LYw;b-(2h+H6_;w-&6D&~GD&@Cp71xgG1=j10&-NO`bJJ9hI zJnu-b`sU_C?FA{uzK}%Yn28>1ngL%+{9t~ItoxG3M=JqE{=>J_&2+@Ui0L{4>y|r} z=g>`KdRm(gnKgYFd}eZvEAQ|p%yxY7DkUB5h`DI;Ba7}jZk9|wN*V8L9=47$7P>`p z2i0O{v5DexIicf4q=h5N!4Xe!(eFRYp?^5Q8?rr-;f^b- zaxN1zj1<{XU7`I7poiFMOg#6OJhOChdQ zb+jru6yraT4w^mOZ$+}GapcMA+#2N#2%w=u{^b2YBNe?qUFCWZusc?R3u*NEMmA3d zcA_CudRkv??Ny+Tl+WNdKIT?@urf2__r9snZMR>-PtO1B=;&x-qVjuAAEbIBUgH8K zSQi6x;5%ht2k5)l3MufuJ^nAH?fz_CQ%UI(IKV)BVqoX7^7ALfpn`VuN3zUw$WG=akN z?z0-~QaarcoeF>AyF-fcHO9|5@)I8sjaec8@F zK28B?0uh3zj4n(J1UjXCeW#WJXl zuVQZ-{clFwfskfMUZc^e^f^FZ(zrMFK{`}1i$6-C++n>>go(*!FC0~jq7tT`y1y6V zi)=E>Rh0Fm>#}MxBq~KlCtXhsQ&BA}w%XMTdZ$tQ=Jn)H^<)XTq`Vv(*rvM@O&wbq zZJRm?6EA#+0Zp}n-Y4hY7)uIU%LcZYDww{LG!TF8AAO0%z7OKbypHYsPXMW zIh@x_^HZO7$N3CJ7ZHfZYzqQ^T4ZhL_NgHn7ZcX5A()kz?q6WSq3ozx_o)JG^qRZ zzA(QO{002B7rNF;oha_XZ5BaE&s-Zm6N(UfRDqkXURKTsJ^C(E45}n1qF69nH4dq$ z?;5L<2Zjj&p4GRbP5qO-mx+64AzX1LkyIi&83BI6s&#+~&NYQ0mPa}OXAs2IR(SN!FYlfT4=TjOb>!B!~pGml{h*)ZuE?OUI z2hVt*_o4^UtB5Zlq`AKJ{tV!l`CNhoiI3NUbBEFKP5Yrd^e3dceg~qs#EnBW5M?c> zrz*cX@GyI>M5kz;Ufl(o0%`Yl$lTXT5(U`$&iESfJ)ZvoS(6>D?ZrZ_?7QaSB+hmI z1o$%};D7SV3=Axfo;Wuj(q1c%6U1pzG*jkDuP+}HKM76lt7!)Z%`sk_5vJ88Roi4t zgV|(KX7d-Ck@4)z%#U)Wf53J{AgiYo_@C`bbV1`Ig03zxMqX1 zXXoR3dbl!GQ&U@Sv)k!=dwpUvW3zmxs#^29)gM5v*VEezQkqacl6khUTH9PoNqLZIGmk*ein`Fy_aCJL-JQcVjI-kvM2RI&Z zis^TEdG}xDae8sKAS8~DNrnt0Cnbr$uor zVJ1kdWYg}^@`QsK%pY-J3ncxVa`zci%q5qGElcp>>0(*qWvhMFJhcaSODX~aXLkhW zhF>QEw+hr<4{!2BO!T#Br7>Rb(b7gTEeJ7&svCwrZuT{GBMv%*rq1tn8C??vbnY`x zXbyJcLKX;w@p-_CFs$&juo_9U;d$V}gtrdjvx2nm2y_sZwjH$MSU_}wRBOzHRdb#` zy)KzDpYs_pu+(5Vo^I(QDLv%ZR+oRiuiT2?ZclzL;X7}Z_+cmFNjMkKeA0p;D4Hq= z_zKf_Km&sO)~wArGOUpn_bPK4Sw31tm5%Ry>-6 zg9Fh%C|=BONU#hBc?k)_^&gbz#a4D^)Hx!G3dsBK>+J>$9$0 zlQJeB8v%^~$>dKU+SU&QjI~*tvvGD5^a)!{NGbs-Kkd%m3ZN&vgnt2xc)vH|Ai6}@ z=+x}2jVB$uXSm!*p7nCwW@@$OkMG~}!~)I_f8uuLhy@g;3mN;BS)0VK~#3u)!?FbL(U3 za;BrNMJlPzU%0a=1Yp+e2|;#VQMddsqAVmiFt;w)_m2X9Hre2h4cpE;jJIPaT|G0Dysqhpz`i4PbbJ0h**Q z@L2U^%YO*D9jj?av0y>Q$r>8=m$u%n zHd|~H>{G(Hy*#?Hv9Td)hki+;rJ*Uzf5}~J+xq1z31KI)1oRGi^liaz!IB;Ul*dAogFi0 zU(2Qluzc#Uu;C#dbqNUxz{FOnmKQ*!q^Aa9$y~4@yar?t5X;Dn^Xfr_Sae9AR`S|4 zc&M=T^uCDsOULB5kB$p(^|=Yr(3tNG!4Boc$TbWi1i!=m6x}LE+_1Vr@oa6+gKu~r zR60b&^`%AcWttG<`TW@pia@O{i}Piwnpw#7J&)dMk$us~sWU(S8zmKQ?`K1OII<}z zLCZX^G|@e!k*b|BhpS#1&4dvHudF{)R-AtTe)Okmk|1C@trn)A3UGPI7rE;QU2B!% zKy!n}hc|b>aq}Aj>OoVrf6LI|O*8aq3V~?w;yX8j{|E7u<3E3*xWE7MdjuPz2-r(I zs1VGk5sVgbWBc^=^*s z(a3~5(8x8oKuS_qHAhh0dW#h{!X7vw2NgjbLnN7?xv44iouH){_ffl41R9WnNOGBi zYizyQA^}93FVa?dTmTtL$k=+L%kNX|W`Ko4#5w}BA?AS=tSHThBQAKAqN8sTYPXL2Dfk$Kz!Ad}X zu^RMHb8$5TLXDHNmCXzl1;u`I%zF}yhTwvjzqCbArG7sYXrc#Mqld^zeBR@k_=3RPtl%c4*30B=Tk4I+#Wa;(leqZG)a8#&rAR#jVyJKDd{rRV>Ko zU?nxfQ?qU^xmV=E?lYb5TSGka_xA_pi__OhlfPyZ@dM~svXp_R|A}^eeS5O9h_W)y z1U6Nbud;t5WB4-h{?t%3?D2lN^ONFc_oPytaJv`Bng^F0$e00jC5Rf4j%Q#gLh(eHQkAhH(bn6xhBFt6g>rL$H9b1iIb=l?s>N(dQ!)m*bZ} zm$4nfdYg+Rbn`DIEU)KMh5QYL9mAGWVu-Hxnq?#X|F8fNt#w9$auT$8vo?SmgRG;Y z^u{{DVL{8%{S;!WNtIBI0qNAesS}|M z1)e_*4Z~IEPF5X$%J_mfRMgP~W?Z@Z^G%Sd>vVsfI~Zc)0iBj!xIz$}|1Uvf`-3It z=qWKD zL0xIHo6%~%G`1|d!33A}Hi|$YkJpX&r@OmNHuq#-IdH%u4dyk|4+-&vLTz@sww>cw zdt&U5qOIN3<%btV)xv>9schZMTo4?@Ci#*biJol0^=R0@PB`dzb zX;q-s-D~&cOYYKWUGzus|NXiA02S=DHx>uzUZ4)jmjcf*P!o(qVFw(_Bfi#i|M-zB zYCiN!Y8*%q)C|QgPuedeUV`~9ncr0as581ocnkC?V5Uc2I=XIeZ(lO7v$L`)fSYsD z4E${w3|S*_abI8ITm?Q}-a3cQPr+i)l0$yPjel|gZQi}ofQQce*yZp)z8K()5sL&# zxnKf#rQ2GZpT7jLD`Nl3VBQ>TYgp3RvOLZXNw?6+0T;nM^Le333j`*9U1;2vElV5> zF}qzy0HgXSt1JW9A(++vrHdnMI}De?s+#Rd|G$s_&wJwlJ8~8*z!fYt3?&LH=Km;6 zzLlpFHp

*{gBZ2XH2a`0aK|C~Di{d2)(kfE%FZixQ0|9Lnb&VYqw+E~+y{zv!f zvVG@;)tqaYJysjD;{3sT|#*}4!AUQK|VMf{SEeN5c~xCi(zqnNv{ z1^@OL);0OsosexPG^6pqVInx zY$3_g@jh9;B2cF53rLUbPT#M->H(M(^!Fpji zYFqG@laq6Kd8v>qI4A)i8$dku{c#5xgN20!0OP1^aavY@KuijX0RWzV{IkwkB|0=T zR8Y0Pz8-KA)<|gnaTWKBCDr_>RcN&bg(ao%YwnB<)!3n<*a%LxCD)OfvozB9Y&;Eb z#E-vB3#%8OWi3^#wEr`WsAxG`y~o`eRJ#&_P8sNyZA)T)-_Xqo{D@izHVJ_RR-U*z z>;M>qRE){1UGg_-DDJi#291p))%9G&o`?j5B0gRN67%Kacqcw@f5I}ky{?n#6hYcg zm-j*RL?tlq0Z<+EEOVL>f+h}d0pmt0&d;zJ^msm<=YB0zPG{9;W}YE-R##TW)B|{! z3uD|`r)oKX!{p+1<>gVS^?)Z#P7c}sj+|XwN9S8Vt@PT zNP@#vwMX+^nBmZVCU6oRUH{A7Tp0fY9)*C1izc>D%^Q&qhg;CRCE#_mJ6$us{~f;C?*5njthwfv!NPTS zaPq`Y;*Ybf<{qE9U*0x@1Gmuw|5KOCOU2E5{BVAnBX!+AIN$-^esWw}cX!?jLlC{` zxE^*W3?@Lo&Nd`1Dr5LjP{x9MXe1eJY1wQq30g((cP!H;s@0{=B zz7&fZ^jq@!w)1E**6$MAa2`+Rk=3eE=h(p@!yRd=zhQ+mgY&}qf6fa{=v|~5)DmKc zjfZ=9B%_w>EpR;M=1BXn0Q46{@b0=a2E2vfbp$~TATIlMTC-lz;}{&?5=#B>cNZ6+ zyh=ku17CA_Qnl;(TWOIKddOoqx~KHlh{*`S^+55^#6d+b=Dlr_S)$ zVZZDaXI#y~LP}LiVz~+HcxN(hnsSO5MR*7ud;TQHR!1uNKZYvKly?cfFlZH4n+{5WJy%3f6aH zTHD0+G;U2_y0qw*JZw7Fo$7CZJe@JR#i>pQ$^+b(kl>*W~KMADj_dwl$L%$4yjzQ3Wx`>K*4zoKZ$x-Tm2v`d+`HajMh z{Fb55b2v5h$TdETvNpYVt;Zkk=o>Dp*uC)H-P38y^QR8qGkWX~PU(DEd=6V_Y0k9r zIX5?}KgJV!2PCn2JQP2F1~QPJwd?JJSyn&BoPb2Ls>NnR3Mz*4=T#YlmXZV zvCH*@$PRGwrP{OPw#>>kh z@iN39FF%WjO$#*7V)XO~C>@V=&i3nf^E*_~kM|$SLF~bDt^Ucy8zwW0m6cUvC3y7m zV(#S(`<+BYs7P;ko%`L-9$QbAv9Wf~&d!$Ww5cYdBPS-nq5uOQaddpt;o*_bZ!Ed_ zH3)>;b_cw@g8vrtTfpT@$RGDV*9SQ{dYa3u)ta@ESCApje-BxHu^FbN z;HPT@Ha>ChviunqO+Kf89)%|y>sSwIp7aONPt#$`c55=vN!Q{*s-dm zWzpr)HIf}h-LO&uy7QmdT^&?i^r4Mm2Y34Ud6JqWEFvbfbYr)&+Ufbgh_uovD+==n z8jg}O46JllcX#Jy?&jdFchL0G#Vy;#iRkGCrx5t@xjQ}ga-YlT^z#{hye+86QJa%8 zFo;d{pjUK{Kic01Es9v<@1zpB#9~f{;+>(InwuqX4SPJ5RaMu&O(A_6I{Vr8B0DI4 zsQFSiI_k&6@kaneZ%Tx~HO+^<5s=8!%!z&TAyMZ1B?G4sbUFV}DO1%Fx&KKkEPbd# ze?c#YB*gm@#QV<2Dx(W#c?*}xp6}uBvc<0cssu4_BCrTQF<%r$AXZ@*&^K`NwA21x z0#FGP-CW2D+k6I-#6S1&qUa>w-+2d6_})#mbaZs%8Jiqd|J;U|K?G%%a9!E^QjLqO zItgwz+FtMPeQaIP!8YR+oV>*L+?txp3FKk`g7|%VvJ9k2^yIE%VAR{(B-N^JZf*_= z3hMJu00t{ zK+vAR1oH3zBOS=a#dUwNb@As9q6du{EQ$K(&nV}Q;F2BQDbNAWo@X`*zas?MCvlIx z-QDdx*wXiLX4C^LEUq1}6v~E%=oT-TV3SXTDszl*NXbSI7rZ0EY| z&!Ey0-YlbXe5ESlXu3irQ5fTohtt6i_z7GM?%tv9;Axwz7{)Bx`*DN&{2x_p@CWt8 zPx=|LSbbzVfo)~@Kc%4XROR1!Hk0?|gAyP&>#qj-QZajJ-;{L4{1y@>Q^m7;4=qv8 zX&_HMR_-ds$~35W*6L3FDAi^$yDvZw$23Z<2MstxAtEun4 z-$YYk-lzW3val_q*>&f~`!2#0n#)o`5%Bk5e|KuElu9j#ecXHrBD+*Y06bWxT-D$s zZM#xjjoQw;ADk=X4T3#-;%qO|Is2Er#~Vq|4LO8iF`rfTV<};`hkR#)qEErY7XPXek>@Dpb}|;yNCmD?X9%_{h1G znV0wa$dlvEJ}>+cF^WmE{ugowe7UKe-R$tJ%fD+}BO!1cL+PAznn+t)uYrmHSXfy5 zteVq_2fC(O%<^NCa+eE-b40#)^$JPoMC2EsllXQ^tVT1jZ{ zquYXQw6hpI7I!j199g67kO;YSmI%0&^>r!Z59NgbS%E%1qba2Z-LQYJ52is%9#jy_ zqu`&@3L5X<913y#W_&*j4}6}7dqMPzrW&6Smimb{C5r^0FwOT&)nNBK+<7d3UbUx%MyLa$f6uhF0sfAT2-KwJ>v$ zjGT&);NZX)sA^Yd=iq4(r=ifJ41zK!5WfSmdQKLK$Ae*!HEVTX6KdLMJ3DLjIi2l!ni#16&6uKw&*cFdD4Rafc$5T~?o~G=h+8ey+ z-bPPjFVpP^lSJX3?!v%aV|dI_Em;EgDHZE+wNukj5AmN-9|WQVS`{hf#QC0=3GbBf zY^_RTf8qAtYrr+k4+pId_~+YlDJSiQP_5M`m{nDAy{AMXsF?Tc5coBg3JvN1w>2kN z@d~29(6cKce&XuGDk&LJ+fY24$8B(y1nyIP48O0~$!QYVnPM7DR6ij#B<)o?C1)wt zO1(My!ZcH(eNM&=W4c}bvlMbg4#|LjP-LF&7leP+;}z%|80XlD0xkuV&J$2~Prl`u z(cEH}#>wv;hLaoYgXl0gu2#2-U-fLcK0kYXgUjL@t9Fm)PEnY6Ih}k?;3FKOBj`|GKa{9}3WMu{LiBlh zHHwjv)OqaY$_7zJ&uKX64J%AkT*<-R>L1>}$A(qN6-G?yXvR)Y#JFGS!j-KY4ull{ z6y>`w9p~X))8Y2yZf92;7&xG@5sv(JNtXNcvzE-_Tdr=8<=478`=_k`9xB&KtP10f z7~~d|m%)r3+@`Qd7RO_~~$O_2~N1vRKz!!;Dvpy(A67&mt512p*l*YNr6;Vm}G4KmS zlvV=+pTHb`b$1s|0?OjZyuIfSLG?MrhqIbjKsKKJYDH!=Y#7*_*?_7srWmkHPE5RH zdD8_R5yQRR0=VrjVAqWRJJ?|yp^$s^cpF&QR#(@C$`td(q;~aJgjSGhr{?A~)YO1@ zptY%`WWloE5ea9;tUgGNJ>XRsK;zrcXYZ$6DbvzcTvvC<#Uw?Jh?E^o5M6Klvj^Yc+`H4l+ZOu&tu>ykQ8!xHMvN8%gLqRLaeCG0Y#=!KPZwV22c;|9^;+qSbR)$ zJRH=_;%iY6rBOfVR|H|Eg6Qbz4LBWUpi{;Kh5tSueqK4D+NyL({txW!lG_cYUx|Xq zz!ym!m5irZQ7$sp_mwCTvAv_ z`c%y|N2-#6VK!6*_bwK!sB79E|n0uulvOB^ML;yqtL1!W~#K)sOcY&V{l=hYu(B%?L40A;94$@`R;AELG zwig$1OxWJND;yYr2eMZIk8`~*Uucz7J5njO78Vfgy$T8d9)V6S?C9(qnvQ@C>F(+( zjkvu8z%)>hE^Y`e!k}?UTE}IH5O@yE?K^f(ZkRh{M37Up!Y3w1o>wPc5=6FjegDCH zJN4>d8o!LR*0KKZvZSi%n3j=lR4YznIdW4~V!^x;)-93!huJxfIg9Z?!!NswGGn|X z@jz^6>#Y%W@q>4b2MtC^<<9%5yuqJ(6f2aH52W;83UITopF{DvB`u<*Ax=?sp&LD} zaOG!fowQq%pQa5Er`W12eMwdA>u6?XM;BHL-`N$jg$KulFcMXL53Fd$<3z*?c=wui zPl$;18G;D0KlyBJXoaW>DOalm{UR1aIKXU+H*5Uc@E=QBufvZDD)w=0PT^M<%VULsaohj`X*i&D}cIohri#J1xI6{n$+WEgV6sHA?t0=ITM&>*> zg>NLmRaEsw<=v8;D*ri*ZnJg`VKJOs`Y4&0 ze{e4IxhZ1!T!931zNjA_IXU`IE+qIfmj`ybRBKZ^R1!G6vduvZuZJVdOOg?Pj-3_L z-1b=M6xcvUy&g}@Ba9Ni1QM{OKsUpE_x1IaCgkWLG`eYCVdB0)6{K&d34$gKBv#Dl zFkQViH@^6Vi2OEUi=C8&H4&8sd`~MY2ndeA0tH}`MOz!2A1f=74+4qU+XOp1^bV>l1G{1&^(O4E9)(g*x z0lF4nU*J2~f=0&N=JqG&Fsp5>yK2fiIx^%B7JvMRc_Kny z1~xYlK0f@-2bPW)2Gu9A-O!D>pKS#N5O%>a$R9o`D4Z+pyd^twr$RtIUz#EcSHFkt zB(O-4cx_BPDt%W|PK}ZJi+5W2qdN9pfg#75$KI4|YIwLkRG=I&i*JBPiMSjYQ>+0E z1R4_T1d3Abbrvl@CCKm{)JBCR$Boqpn(>FnI}8J3%e z2YB%uHoHK^pzGbK07Wv}Wl$^5>B92QtH_++HrCbx0_#Ldnwoqjp>jGpf)$zZ@%CVX z{~FT-;_)i2>gg28LZ%>pxEnSU^b)|rtiede98{9uSY$8?U8$=iBw{=#gYHLYGA{gKAnqoealEcJE@ya6q z)7SO8;KFF}z@Qs{*&3}7AzKf3OIc_UT1L!bC+Kl|Ljmws3;efOvAx$3y~6_e=&#X| z(1C35#)gk9u6^C~)zc;pR~z5qqyx>R`_Y+jx2=e6_$(q-^fYEeE{oiLH}>h_TJ_!T z@e~Zr6bUL5rI%cKB$)FM`mBev&t$VC zumAwMa!nN$7xKu+sMhJ|XxK;S8gA(Y(E0#^Wn8cKE5R*BDT6Ger26tY2_f(NU*AF0 z-{|G~klIIWEF!c-~Z(Z)nRH8XsNgoYy#*hS9a;-~if*K`148^xt}u*N}$g3CA> z(sXsWhR@BsX;pHoaJMj}(Cu@IbDp5val!Jtl(Gew%r>mEZxa)NhV&nCWGbqT2gKj% zCA9*PJ4R~x!nEJtn%4MtAjiY(f^O(c2|{nVlT=MRbS5t+0}2ItRBD)xSv4O4WB)JM z)m>yyM-bnvX+FkI`+cr*Z5uc*UVIkxMutUSl=j0_HPBn7W{7pFRbS1YM@Jo>7($>}8y$^W6j$>!1 zrc~5MevwuX1H*0ZWxW5*5oW^{pni(FJtvcBA|aqb19+?Pu#C~DW4E@huIfr4;OiDO z3=CKqqusu>U9_3Q4^;0&ihF;xfPp1%QeY2|SviW?d{cr6&i%4=M?z-A?Em-?ZGXn) zlwyS222qv0xsdGFc8Wubs;&eCtGuz--am5@r5QAeiUHdlSQe(KX?|E&Pm|Kv6?Euy zLDdrS^@NXq268`dpFKnn4DO*fjtLQ%ZiNX6al`V_^^pr!s_3^t?<7)Or5EdBZ)bUX zU#5T0NrD68bso{s5$zl41Y@0pt@Xle{hQu64n`UXq+O8HSyiX9t`zAv2w4+4s zZ(wa;1>N%t;Y@yF%o@Ac4}Dl_Sl47#%JodkZA`HAedB#;YoAtVJ59@(x!Po0;&Whw zq^P~Zx6HLzn(~$DkU|+(BKDx z`iLTKg~~)8!RsG^TY7S;X=6IG0_TF-M}#$<3}3;$irk>$VmYQr4-<%Pg9lT5_+uU0 z1lCRIuuw}QzwdusDOHpug%4rq0hKv8*l3qM4vKxYyC8^rJg>f&OsW=%22etExkqhm z=uxszQMnvkmja5#!`N66$au6@weO*!0-qyeij1qgj{|rOmsJc}3 zS$U>0%Qx192?dc;rr*Hi5IB$4(C(3aQdV24uBmBZbvvvHQS?F)@UYqB#q|g$QQ(KZ z9zaa>^vSL#1t|*CF93m!`CX_;S5Se*&+om^oI!Dmye$LR{ynWTWN)#g%ReZu-#RpT@2O**U^(b{7ura$c`e*YFmYA*l<_9PNd46pn6!yIQ3V?4? zp*G8fBA_|Uei)aR;Rb37o!01xi1E3(xf=jYK7P_ym(l1TN%) z6BjB0INkH1$Td_|?dqOd9NqG2<-|%-FUfa({!By`PPF^+d7V)(Ds~8y===HB~p^n0#exLZN!EAjqj$N_#HM zA!|t&RiF2h^^~y6ssvT3Wu)*sI2=z$a@GjZR)pq$D$Ve*6Gw7iw_Kdg2i^of7Na*w zMCAK2*47m(Hk!J_Q%D3$-_s0`V=R^nxuprzbc=KMJz1Qk7*EfgGPvE0+KUShh1H{2RcCDIaWlepTGEk--CXS zJ&_B0LiAfwmd@RMAJH`I^Zf`iqWT8; zQ@b9;E<40|O>9p!_breSyl;_EI)LU_+dn&TuN-1unDZL6yis0T*4EdfzdbB$%%BiV zjLBy^H@N^j&?W);2xyz10H=yUe2NhqoN51AK#w(L8@jkGjzs2zuqNKA@2Lm}8Q zI9FN~TBwa`O+_RHXnKC!4|4;6n035eT3pmJY@OSO-pC01-z&SJKIi_zbY}xd72+um zik8p{>w-3U1Udzzb0d0s!P`UZcwrEcYx_o15EE5*v~7ST9yl7-p+vzHoCYC;6wl&v z7a81ASvHTM8a0kd^F>Ee^>KRa%TGN&bqM53m4bQ`ZdSFO%5l5=(uy1$9_?2iQ?=Wa z20_0~x8Gz@x}L(2cGvDd(r^yL&2I@(dtV8uJdB_}7C??~xv*&FW3hx_Q$LHjT^cnw zA0RN7UiUVgqh}R#MS~5*l1<|QC z4vhB2lkIL&L@cowWera7ZzA(cEM*@3CV#LJKu5~?L3a}3+lC+25*-Eh^^*C&iyoHG zQT2xHYj|z^8gKEF(sE<-Hw^3Q8V?M#3hCpj%gY+No3E!UzP}&eyPNwy z81GChG*z_282f~DsWN_(e1*=E(ZXkaC_Z{Q67BNM#j{hu=OmKSqYM>4`Fe`z@hI|_ zf6OgeZ2O=n+|$2*XY2jsv4}#t>xTS(MwtG~bqkik^knFefMBB-7>EO3TDL36ah%rt zoX?>zqjN}UuZH@{n~@yY{q!uK%?sq@{$n$Q>Z$6Wc(Bl@ZJ8P`0?rwEAJX<7#9e6Z z_<7$$774kGp(NO?Yt-loczrJcGhrbOP@dy~+VI}x436TL7Y7!@#v!kxd3-iQ(8T&} za1as`(c>Q0_+mKNppW{?qlCDO0T`sUR-W#-aIqnZ0|2W$9#3xAVYvWPBPP6dUs0c! z*nk+Kd!cCjcw@C(X9zEZ6^VqO>iztHh(s2K>+hp`W8mnt({{EVbjricJ)jK{1mhm+ ze%esk)kQ!aQpL!`SgAHgD&&D>Ve$RPK49K9v_8xZq@<`7#Q!LhhaFz@dlfcH;QBHb z`WLE_OvGK7hX>V*omH>F2q1SryaW2wxH3y>@?S*cjcs zg&CQF?yrLe*iaTJHfQ0T9^`#Fjf2ZMJA#g0mj0sc4#b&ZXfItU)^d1QZAAdy6Q%X@lA@>YP3)O02Q z7{)(=KyDEF1{BVH0HR=&#$;NPl1_~IsFXvJ@=a7sSh5^FF(mAJXXt`PsOkAiO@t95 zyC7~oVKL{j@;iZc4VBOj>Rs|xjy#2q?oLwLZpnE`6!@+3CgpgoxegZ5Bd$ouxv54H zRupHS%YW$bAyMPV69&KJ6cr#h9q|{L^Xs=j;q{)Skpc0mfNJrK9I= zv8Vv}Z60^i+^ZD7h3YvY^6di}&vw6Tllxty;mn9Wi0t4UQ=81wZtyW2x-dyjPfZEMLJj$Glk;P9NfiTq6*n{4 z-!<54U+b?y6z4>!-O#Of9pCZNnj<(-&(PchoSZ(Yno=F4V}M+g%*;Q>f-#lLXbF8x zo5uO}+`TJ$cb&1RpR90x%UOAbmFuAMy{ zVrnXDBL)|7jXX(g@9*nvi*DT?aooe6fA!oZXT0^%gtq2=1m&SlR@w}Ytm1<{JP9PC z8^3na{(R09WaaO7s{#dPKIGRrO|Gkvm{iJhfE1xOx*Z5Y1Bw&qb4X;Yvif?qs))); z7;5T~<$B$2oA%$~al}Ap5EzyR@C6<-AoIF%x@thgTeNK#SfS>-ySvkEw}q4BLC-&h ziHACq@aqI;jGVk&x91gBB8}E&gYgSEch|&D=2LS8OyPEwarF4YQC-S>Po$_Ic+da2 z{66fMb<5r46`Kd=r{)fJos#358F2Sdl;YtL+@_|b$xt^qZ1GZE;41_rha@NO2sU9b zU_Y*}LXy(-!2_0oBgq<1Gfr8(F9yNToJH~Hhvz%rqarfi)E|?R4BM|SfZa8Qd`BXB z>N0z=w(9ab@PxCK-157!;nw>Yd~y+Y5&VK6FgFa;z;h`e@CP_j48#tPN4l=jZrYsLUK20&W6eD$?^9unB51HQ)^+acI1Zjg@u58z?F$ z#GOGtu@DG@h(%hIjb+3f8c6W%$3zlt`CdfEr`sjm(ks=3u6953jl*=`9k06D!i>zc ziN*+RXuj}(%VT7{Wpf{D$Hk~N=bjA`i@g2)G+vBuijw^$R(>Ljf={|)1}{>Qqj}j@ zFE^u!(@aArY^6^Q<^|J#U*jpgL}zW)2~<)trZ`}^}A z86PmNT*rR_#AW~_sU`$;RDr{`YaLEO-9DfqFN0Yr)@TusRitD?i;FG8zA)?~L9?ue zZOrf9Nq~wo_Cu`;*wJ{h+f9`Q!r9_sOWa&RucG``p6Je@P#&9~9o^l<;(hm5JLxdA zyW&;_*y81y^*XS4#Bnk|78d@@U*RILE2zSBpPCY+xgT89P>MM3Pk>pFj)o>Ex4g3Q zqh}fjD}(e|ZSWUCP?eRHl~PTHIT%W)%^~6>FFCuqvJf)ud?Q{^U_6dE=F5K( zWjx&K0EI#jG&eRLbj=k1@hdbO2>HP}17;3(!d1~x8Z2v-AA#`PsJvY2MSQ zb2tJOw9mk>!n6yUJ~2EvZmqZ55B>yf8-3sq)z=3_C^cm6xRBz#2TbP6{mDIG)cXdU zS+YXy6nKg0nk!%)?{u;7^Fu`s2$1c*0(FdKQ)bJvARC{4x~SHSj6d1& z*Qd+_p4nJs%0MHCgx;R1$ovRw&+EO3-GTeCSeS=j-I9!{ribFNMr=PHO*9P;7Ibyc zHxxo=-{1x`q$Tn)A(pe0IaXjAEM+L=As%YOc&qEL5t>YT zr1x25lmrCCi38d=-Vg#=3NnMVq)5iaJoUC2#c&4*3y0ByP>Fd3(cR7)exQ>^&Q6s& zKHDY>LSN$TDdbcLofH`_s1@)L<)V0=+a429$ce~*7Tg%|??=iBD=ailA03$Zg88jX z?sG~G zA>Gm?-HoJlNk~a|N=bJ~+~wZq+r+&vGyMV zO{ObPyO?x&#MN~pd_2jlv@S;pnKe7^0#wDqQw^)pgf4> z;HGeoThhN3=j?=Llf}fobh1d01`82J+|xuARM} zR-PcCrm9Qj8)2jeL)DQ7MXjfbL;lB?6{|OizLq?iclkf*i(r%vbR6E8N(MO63q6u2 zrDbut8aP1eTiZeV?cb=C{3i8DFJ_?I+zsbfxiFp{DtaR&yY6wZE13~5n^NF8 zJhb8Y0xseIy%ut@?ObVInx2x(KJlknbqh>BzB*Sag{x4!ce``r663{??E|PHB{zcR+A=Yt&%9TIQxYG!Wmv2 zcK`P_{m(VNbc4I46`1DjR6O-WRUaMqBcB-2==4D%r39ttDfc5AM|zuxcNZHE%bS#+ z^^@xG-|5MmXY`zCd(DV&r^J#6-Rx-^jtW=Me}LBxn%B2z6RTyJFzTW6XG0$yu5>TH zE<9t`lkT?n0aB6I_jhTrYG1JToJflBGjzY7`cg83sE69cr+naE`M(zy89>m+011a* zi5Pl%285_Iolmt4Z1hx*eqmgCQoVJb{SuvRfF6}VyO%<+F0-6pD(UNv;;1P^^T~(K zj0Lv$J=rKQom^u2((}72%67)k*Cu&o6*gruzpg<06--wEl7PZ&sB(z7J!@Q2^v%^M z;oIm#a4?RPb^8B3zTkmwgtasX)$_=Xpf3fzo$`6k&g_chiV(w&Y_X_&Fcy+%VSntf zsrl0z;aWZZj!l%5QT*Q@0@lelt~OBe|MPDC?~C!IN{-0>pSKmfAr3Mc%Fo4lPg|Wd zXPs<{V>Ks*OvgNukmL6WnFe>{9rq?GDXCE1O;$*bw@)C43y#1}eOJ5k&OL70CsPV)u+e_i$vwKE<@cs}?#AejMc*ukV1sOM5L9kCCQSAJ$QR1)T z`k?-@7CTrmUDT-3E*0+tgS;fnwZk={@ATAu|B|tj%rk|HQ80x zf7yDiE2e(={QupnV3;uFR26{|YtcRd@}q&mY#^M1MhGOS^`?f26CrPB@!Wq6Z9qS} z`*v_WU%KM+A^O}Dj~99Q8gZ^}_XA_yM&0wCuVA*>+&GVoaE@1IgJ0AjnGg#mbsI^C zUwoH0Nr&UhD#d3wXGh1Kz1_3r28X})lfPv;Qm}DeU60;B4T)hIVp7u^&VbrYV{QsX z&9Y-PJD}~|*Mj=*G^~{+`Q=hlnYkRS{EcL|q&4=-moYKOz}@cuf?BZj<({FZc)!3| zX$8LZl}<@RV}5m&nTF=a3u~TRM(B-~846gXE2f`Tb&q+fz*$ah3c>nGj^zYDKzH0< zEb#cZ$Len|QpbBFj>q zSq#xbqDTLtYgNO7e2h08mDur9MT^YAhG|z$IyRX4EELbHjFiH^CjB~7#FYoEr>YQZ zu(txkMuERK#pq@972VekafG{u00}(^0uLLPl+Trql_DrSYh`|L*=}B5xQmQ(%3W*o zq`i))q6+*Gj9p9L-mb_}5-W5dXIm40oG+uOLI* z@yA3#NXXUTEO)uGqhx7~mxsA4cz8+$oo}b4vNR-=Yr~b{&+^gJebRFq1w3&3S+_>Z z-G|@ly-fnv8(_r}4tNQU&Z_MGx;FfUDpCPVry-!CmRD6Zf`2V7z~d$J;O6G`1rU=D z<|^?_o`>Se0G^kZoBJ7sZWc0%iHIdNteUWpXG1pfxpTY02CNJOpQjt7&FmUR`dVg4 zc3R}8pNh@hgMfjwTDR%&rczT}oFt^zA-)Gq%4U*oetu?(k&So1&*&#oc6$!h+L8U^ zR-pWQ>YQ(I!jtA#Ji6~MeZJo9rsHs&kx(saa>9s+bc6IcAa!z>yO#vnRxEu)JpZ-k zN~Vi&g0VHD88bMZ{Qc5Z+|W~bn!(0>B2Ix>ze7?%&+e*3p(+}-(;J6`WIUa-XMMe= zC-BmT`f7-RWt!Gk@kvhAjYR0be)NRX|M5WRhrUsp<>J(5pkY|QJlxBKJB}+WFAVo* zk$E6=eqtyH- z>u;0q>DD4v^mPChJlw=g@EvJgd_k0S_|2BOo2!c*Mw;;SjRFg#Q7G7;3f+DEI~SmC zL|i|DhiALoVYS>?4q60Na`W@^sHEHJX=$aUr9^mmKyvB)SyAz4rlhw-VtpMR&08#3 z8mq(Y-}HvY<}*ZHb5Kuf4E~qQOt`Gsxj86kXe-b27yx`uDpgd-6@I?UH6Z$=L-1N3 z4J*?<&;4tI^ni7v^e@Pf+zVdFm(Y7lDQatev;Eu2aWtkexp^LMl-2$mWlYdfKreYv zXh|{drfWZUwwxLU|oSPc+3xpRYhWxm}|gufkKyy3OU1DG*c z&Ci#p>xy^T<m`Y2Ow(5 zzua8xe6GWC3JNUbz~Sce@=82}qmjlD@#0o2w*&U%)JwZ$SihFi@(S;d1Jt$_~N$iac#zD6QbcgUAMe^D*muQ;)_mLnR4OM1YMb@ca=1SSvRF;_r z&H1qD=SnWJXFl3Fyfg4wA9Vdv4D@G^Vemhoo>95&+fDsQ^Ma1~;n2w-_(E^->^Udm zs{z(p&WLD}{8=KxLxI)IEU<_dzY9U}kA|p86W=YM|iJ$vEhJS(|Cv z9aw=YNK`9*{$DL%)nCVvkDRKoPCt2&draK>gwM@Cr+J--e7ZE(^s}s~1--oqx%-LR z`pZ(=Tanvat{EL6hZ5m9`Y>bjPkl_%6@(RU+YvMtrh;q@h0IOn2(C0^RifWlR8`o} zI_nQ5lLVyWeAO>z9B=WC;io)LGuV6VoPbX3`2C47CCS;X$w}J{i~XtPViy>aGM%AT zN-Q&NOla@J2clTglG57BU^>`*pNr*X#2kOn&z0=qPG(vCfpz1L3f6aVoikmDStg=* z0E0PsPXwr!RFCefSMC?nG$tlg3gfK0?nH!yaijvm);sSvN$Wsb{I~IbD#0hJ9cYuF z?DCls>n#H>K>q!1aRy{sFp!M~#3jPUJO`Zl%p|=Vty? z$7N9|vQ23w-1kbne$H_>+P0=&bA4;AFE7sR!l%cv zw*KT#a|b z7}|u+P@olkM5Ob}I{bv8+}2ruSgtq4?s{U9cb?!gdu_*0BwvI3cTFGE!!}ety5=M$ zCL^HLMQ@RQNqQW`OXk+~%yF^*zLj=;=wlSBsY%pyMsB7$4v|M1Pb#vvXMXBxmLDWi zY<`SXC)d+&v~Z(_ZYU?mv^p9)&N4uY{<~yU*63o_`Ylw_@eZXbV+!@d-qz{ev7ibB z*rJ`BE{Nu9&%P0WfK*voS>SgDxf2%4*QL}~?T|$t_dH{}I?^#i7B#442gkjaz4c}j zq{<0a6%1ShYTHhdkr}B8N1_i^1&`5eS4PUI+cU}g`@JbHYg+1_#-8hAfv_6knR4>Y zDbEdbpgQ*W{0;MV_=BRXx~e+O+tJZcx=BZpKW{rKDn3L=|K7B07(WcPXp!(LFW4|P!R`C_-;=9>K{}J?Gl4fp9+f%1!Q!pDv>c`w{fJf?H z7au{s0FHX3nkqXqo}~iA0V8E_6Ie<5Za!4zs35Sjp`T4EB6nFL1MV8fEe%rJjIzhX z-7ikDG7)q4j|2V7lS|}i^G7gPq+|K|iz+$M0I6F-o+jhi-?FFBP9(O*ZT<|87JLa*3vuzj=SS_O#YItbBV~-I!-Ipo#PeoO?h_pRdx-Q% zH<8Lw%^FsEM+ZAQ6u@n^n;@pITXC6eYqDu=)e>3~EdnfUpF*=Ao*@tAlazA$(kXoz zP((W=&VsSbK{hQe{DUoEBZ4umq8#UK%N@&F{N~p(EfzQ`Q<1998&tJ-KhUQy&*=;G zcg>SS_M@z0O(718j3BI)7WpnimnE_anPv*zaILM11gL8actwya|Z{`lj^X0mIkK1pO%z*SQ!Fs7uIV@CJ;EEES$DlK@bdy;Gg`&$cd z!r0R{xKb93o_8695MyuT=cY8?iW>6q(G!HaJud(o8ps8&<}j(}>iGK8c0eh^n(YiI zT`&Sy)ILD!Da$_KfwA_O?Syx9GZg-n$dbgJ0DbtB|H1JW3La+e2^ZZ5INQ@7<3H;6 zbSV?CR9UvNj^xP$z!EAd5-0_oAN&Ek_&(=^9U2oh#BnpsaZ}9&j_EBd7b87AXizFE zpDx;^U}$S=pZi=d)|Zu?)w};2NePjZB0#FR{s)t#)#B_ykB^HBk~BBp1F3|5sAN1X zJ$;cNkpvva)UTyfkw~f|^UKsvl zaNPIi_%lM3*p2X(5Aj>RXQrdrn7NQB#%2AH1^t%#jjSUDQKrpl4_YSjMI*#rHUoluzRO>1FwwdqaTcJ%PqbJDM^4YP=7 z%i2-g|ISo(W%v4<&@3zp46-A~ z*}9E?17F-paPPskv~2+M?=U47lpT&3Z}x^X4To9ISY0;CKvkcevRvguGJHK4l#qZ! z-yBZ~S&7-$W+%w?iKI z<$=P7u3MOn9K(7$iyn8|1ZAI{tOKC>9(N?Tx0aTDMyoR@3$tK|hKKi%1Aei9Ho3iH>+>jPOdvSJ`1 z*6F&jX6y{@>mRST4N(c8s^3@?#-x7HIdHI1Rr%z-bvbv^E~FgyHTFi_V@y^?Rl;wu zkpT?F`V(A71qT5Bh%+o~z`<5G@mIWimKm7D`?H{b2OAa3VIvr}%6lJ(FtqT;f+dn# z*!{EfeyD3abz>o)P6A?tAacYD)vKR`J^DjDfBZL!qTo?s8B^3l=44wZAx-(C^s_R< z=h!#ym{f%ugE;tEx7R`UBiWtrjv4+v#vvUHI#tqxqsGQh^yEd!&RBa;ct=jRQipc8 z$S8!7AvkKg=`8S0S&fCx7!E3;h3p89HFw=p;^7WHT@voGo+i(#Rf#Y0TzO2ccNz++ z)-Aq66TCbAvq84*$^(F>v-9)PPUJE%j$phFnR@L1P7Hu?{q9OlAXPbm!RN)I($b@2 zL2T=Ulplk$vhO3XT;^b4R^j`X2M70lP3AV?k-(B|hGRy+ z(#yL48hT;xumof4VYD&VDk}<$GjgiwPB@Efu(=hyJ90~)wUL{MLlqcNLs;m3uJ{*7*~Pgao~Y9aCR-WxsG5A1ikf@+d#e2IXF-ol*r(H%r`s@cIsqVHaCXu;rif)C6YEMS zNH4^xsw*$AAq~f&1MBqw@8M8Szj@3BlPcoOO8ue-ROZNT>?po1#5e{UTYIsYeaz3c zSPd(b18=sUJw-htJFO4@0MUwD8QaF21AGiYRi{@@WtBQF9ymvJK+H0{RpPr zfwR7bQO8nr7!%s@!{=HIsx6tk?Xhn~MIpiMtk5h=oi_U+h*(|z&)!!h-LEXK6qcJxWF-ot&zCR z`{_Ka9%3eZkMt)O8}C{#kw^_+lA#?rZI7IpfrfngSrL~{!glz;C736A+Q9Z)8~K%h za}Yj5VJ)M&BZ$BT(`*G?4m{#GnaVmJ$?AkV`b*rGil*O=$%q1Mywp|ZpXy~7@j#aQ zYOAYi*rR}&!fBuh9n!h!d&?ouAAZo}>O?B74p6{ByM`5te~)U3j31fc&~ z4;El*zhicGvmksQNWyI*6Q~y)*51CK#)H0LR?s~zg!GY=*>|}`2cOhnKyA!cSOp!> z_?5STRlLWa3}$^{W(Eln<>ucr%vgQeyDsUH4n zj3oT!tIFFobaK7K=S1?0(ar5`74<*&M@8Ux&_#MhpP$Pp8Z2q?-b113jbWyN(7fH6HIe-P{CVmlQf7;6(fZ=3AJv}8BP?3RftD? z3nuCAAy|R2-I6tx{R?-%h}iY9jK0FMaWGOrj>)W5h8hhI{!O72#PEZXOF8_OR98^O z02h08b@Cc-U0f}O-n)pmVmkT|_tLJP)IY8&v^59iC~MVHjt_OsB%rR-Wov6W_M%S=s0?Y_CWX?H(`$-<8@mmmgHyTil75L8*xh4~oiYzAGD zSg%mx8zCxnQ(=d-`rYmx9tJJ$YqRC5q6MbgC4!|XMxz`i^fb$S!fwzDqUnqghoi>{|)Fl%r?WL=*#HpI)r_Hh57foEY z^#_#`G_UJ`g*+G3mK>!ex&Ec=DK8YT=RTe}q=Ym6CvN%{H0`#Sy4ZvY2{$2ax@xG( zgm{(?PG(evykb=A>a0{hfBT3eeZ4isSt#9ISR#xXCkXyR**eO9yzhgn@{Erl1#b7ruM95cOq!%Q}HvqvR`G4EZ;Y%@X>{9TU4G=Lht zsh_ybWnb4lDLe{H|F-ufJ}x9Uou{T_Lc~b6%>~U~59OZ8<>bBaa0jKyKP5b7;BOOS zWGngqa>;y19_p2ZYMhlMA>t4)3%F)SvU=rx2nna7+C$c8I~Vi-4h~S>a}xw5#W!@B zYt-TwFL&+ST78-nNh5B{!uX>0=a(wfA{#qynSpZDHjMfJ135 zCk9?6)w-9STm*7c8+ul@UL=&wtv%vZx$TJ+@dS$WKQUhw5okRZD`vT3%AzA8O!LYUKXG986KKW{HohK zeE(Sei9mv!$Q3zrxjzR2+=;DS#-TQkxZCLN4&+f^TD;1fGCFgOZQBjVzg=+t^ zk9#5}=8CcN!wW14Uq(`DjqB)F!FNorLA;7UpN=bFXfUo77z&4Jwy4G_4e9^*v@|#8 zc9>iacs7le!(3#N6^HO2s6w#0}Twnj?Rn{>Q2Y4zCcK51S)^P zQridbg7>i{m@}`@{|-R#V`CWuKOPD{(^J5+cm>tZe{+m8@dya}k36Bu);YH3%3xK| zDHtkNVdt~?+FC{oZ*e5QM%HlPsSEfT%%@-&Ign{IXV1umj#%lYS4804VaHpUZm^cW(nLu`+=5s_Rrp6q$KhBE(uEc8O1a z?YH$$=32Hg8d6oRAkFxzm`C5m#LM4R@dpDdOn*L>6Huks`25c#v#F0gMm~cU1cOn+$;;oyBAC>g4ng@F6VJ`pbU} z7TrYu@oYq3i}EP7YY z^{AM8ic6^@=*>XiRV^?p`gf1*T9e6Keqa%MOKJx>hdXx6Y&dC2hLoEXtef@Qr=KcM z!~70bKg8w#j?H0h?^_qNK-gTiW+Im`zEQa`Z6|QLhNQ(*?n-si=H|X}wS$F}c2J>( zAV6AOu5A6r0es&H@fAS);A3HDH{x<&fkKa*ZQ!BrbP9%Jj!}n%hGYeaZ76haXd=6Y zdP@=q^s#OEabdaCG5a1b4(sw7+p2n)K1L^ZECaDi3Q@ReLZ(qMi{I|Y&lF&IcfQA$ z!$rryc;`LqA=kSH(ECzSQqm$LK)`*+$46aSgzyHzmCvQTxOf*VossH*T+dyp*+0Eh zpFZGb9trtdV{j?uZftg)U0;ZzeNa>#0Tx-NxATYPYRtD_q@h;pIf)o8GYTfd$O+tO z9Bu{4O%5o>5nMD)uUw_xp?<_PQk=XCRf2e^){bcenoY`JZ&SR5GL^; zgG%ZVc<;VOyl7fjczA$Ki+SNGJq?CE&Ckyt9%>cBmlBw2+w@ z8P%Vf67fYxBLL~k$Y@I3^Vv_>_<^R75c{KMq;#E|r`F9ZdelWza5!9p7zUNDZ~UWv zhifD(iCG9TaAO17F$vr^adsZ_oV{zZkUEI8cb}`U1>WF}6Ed5^u7>57{>~8JYK8Xs z@H88gXD4|j%E`UU-^u@#`_F9o%*2EQC`G3Z!Oo8q9o5(*;6@Q@$J1fbv$K7V7wbVM`RS7hm`PFyh~YxzdliJ&m)D`Sk^jTC zF}-}~gmV-`*Vm!C=u-p$Le+Zh<|B#4qp7S(3FsDj_(3BInHb&V8LaBj|Fsp~`yI@~ zq+|4WKA*p546A6I*N7Uu9Ud!q{J)NF2pN(^)l0Covo|~*M&W93@!Q9tZ*FA^y{BMp zHy{5FcsNHeI^TCzKkbZKFG*mJMcet0;N|QC$6SV0B(YCAx1-5Dd~H_~iq^mvSef6_ zIwO`$2vCuFZnV7)u}<>dDFAE-sCKF{qZQpzUt3W{>KRSDhvE?#HmU~-zkwX&r{hL zhv1MN#%6wRZTsGq!yr_oWz`^&LZ#(~d++$0dnsI*3ViS6Rb^?E3d@wU>&iwR?49|# zXZdCxV@2evXEJOD_8f6*6W;kEz}*;jgH9&e*r0BS(GX~Df%8*TqE_x$HbvCYVAF$d zS4qApfwrXolLVomsy6Wec*~tb;uSJ8Y%^hCD!2$cL@iIKyviCcp@mos~?Q#NNDG5 z`<&T?m&X?ua7#Z;DYzB>=&xA*CXNLMV~1xB+k7K51cR8E7&yBh9=^gMzrtVCY5=<# zI0am6NVL%Q6a?#jR;V9g{KKN9bO7e)!EaRHFJSC9 zSc&)n^OC7}8ukwLvim0kAMp`eqH(+M})q#Jk zz0|rhKH_mk0UUA8r2Mxg6&@6~H;}adoE|_dRwLHc6l|u-Zmflo@XA2I+gnuq~YY3c%3VO{a5L<-9JuQZN7>Y&Xu9PxrKC#|sex+s9u%9zT7O2%I- zAo)Q|Oi6q|u?LstSd;-;{ur0p*0!D@AyFDfk)_-p~_M9DddVk$N zJzHH`TFO|;(9V9H5a1M)N*OObek}aKNfwG%7-akjn>x}n#&zJB7Q;q)o~M!IVj|(h z`wP)Kj+(Mr>m9fY+iz6P<72|eSk{*IA_pnL4vp6_`HAGS3%c*y9x4o@Low+Dcwl}Q z0xmV&=Uzkh^nmaUS%@puA%=&xp1eZ|_|GWENte^NC+=?Z03>C=7KRY(-i)_OMn-z&l&>mFuf z3DlPTZNg|tMmV|IXP5eOsCsT*Pcf%!ANn5QqQOA>b>W!JbwM++C$(SWxk#$@0ERsyTI#o{$&8JC$8b0_=GwcD z+Ulujs9}h9+?viE30z(lTwh!$CZMC+S0$kS-JSorNv{yyH4t%p!pRdB+0B-1f8X!q zn3Hq(Cs?Z3li57GziuY8Mzj0J5`-_>|sJ_u4+Eh^!>VBjdhDh)y!q1fm_lpV*%~(10+_-!B z|6IVoKMzY!ZBVT`qE)CGr{)CmM*gVJUBP4hb@kr@uTQ+%#C?w)uc;|e-Ek#u&en4< z{_}9jqCQ1z3|kdZ0V0-oHWoaV4!f(X zQpJ%DZ1+pj9Yr-iYb7r6)zGl7O@8RVTQTO)@4y_$@t-SVFRiXNDV1Sd?sqWE)n5@( z5!}EIrXwO6b(h->{cTFAQjsH>e7#L`OCq@aRe532ynqQ6YH?2BDmwr*1VD*CajW+l z7pYl`39V)&0RB!R7m-HKe}1G;FBJZNF8!5{HTo~=!T2kCkwwG6ic^Vj$?fW~t^;e5SsP}RoH4oOZzIJ8>%xuu8Szd3!Pa&P2xFqLFrwPR~K%1?+Gkvd8 zoXox-L^E(1i!?M6=&U$gF{gZDzC1NqSXceLU;1*`7%i2EA1GFV$(yozR4IeE|CBGgGB%B<7lRX?yhpoIcVwE=!`QhG8y z6$>W_1q8vzlmQ2P{t=PoRb{`Xr!jGGX!=2}NJmu_m<>k$I=`@hM#eFHwy&FXuIPMBn1YUr(v*Cl8g79=P#9_C?MGrXPSGx6^4UP%Zm52S%HL7W8mw zHiq@do_TWQW%sIgbkX^qq+rl9KPpAlr(aol1v5w2?a2t2jW?^=xz?WS0q|M`+)t-x zf1O|XtlDknsA_2N_zi-vv134BuGVilgddFgw>XNRgEu~a^@L>LOp67Br`Z86`SIsv z{WtV1&5JAF4WvZPd4xkvhIsxo|8jJI3Rui8lO7N_AcGNkui`(sMk3FEpT}BHzBr~ z2t#LM*4&G{xg_6i0Ky1MufAmdwo=S1zF)4#$$vMn`(gH#au!!CC|o6lM#CO}_D~gS zI$mqaO7d@3&(3uL0e!{8)}YlfsHlkgDgju^&G+_-i_Lx+f;5ee4uFG!141myr{zgY zc9ATwfsB5xx3eQr@-S(V($dz_4`2+y&=K~sxPDNFrbWg)5so4oM=Ose;uUPRUkM8a zsQ+KneNf-g$ld|G%f~?o3K9~Ms#F#bu=iH7kD5iDpumW?sj=PLLjTPeHxUI5!@vmj zQ+VTH%06bkp_>})A>j&)XZu5vCkU#zf5yg3%gh80ygot4NkkMB*c$){D>yy9?8_JB7K;2d zWQu1}k+4$-K&qBkodLSvM23krpuHIH4P2N2hR$m1)SpYUJ21p1eh<sjg7GQ8}0W{E;1tr{2gdftTuK>h8BN2HHR`pG{TbQ z$pMx=L^z(s;Vh5cnzkDk;5*`DpFNKcrtBqh-ud8SFP!M`hNBR{#hTt&hcDD_fE1`t z8q#|fTM(0ykT{1yYzZ4E;>*RL*BT^{kc&ajnN-Y zCg5+o^tO^F8c*UK8JJYJy|v}xX>M(8Ei1EZOCO3OK|1SN$>98Zduo=Wt788(R8C$8 zez&!WP~!K#L%|&8v#-&>N=TGcO|E@6YaPcIFkJ)!6j52KgU&a>CknWd$G1y9@2#{6 zo<8ZYm6E^>{o4cgUZIf;k;5RB%jYI?{^+ozz%e1&wq}p@P8jFWx(*1;7>CS8F?5Rk zCOQ9r7Qt9uWizLaqs7}0Z=S2)dx6t#U((l!2j$EeMR?1iWM^~6caU;HUQ|}qCiNkG z#^2dNNdw*Ru6p)YQAHEyU-Fb)GVSeKK8r(ir?v?*RaLBOZ?KL{opf}8Im81|ht)br zl$YzHg~`>i)#slnMLRnOWtLzd0t_MszdpfUkq{IAYp(wK<+waP{`bYN%@Z3N``Xp5 zRfNDq0whaJI`Xwk(oJ|0*H4kh}jk1ba7?yZrst zX-;jhJ0Wy=|Kzy8=@K5*Vpi=NLH(VLm4fl1@#aU}f-(r10IwI9mv;|l3Wt}z;nj}} za##}jWvn@psg6!{it||EhRO_cbar#?R`9bY^QKbYr;&(2n@*HdeuYHHBSv0XYNpA; zOLN{@{3>FyX3h7;#aNLh#KM%N1sP384Ay*Udu%E||mo2&OHY8DkwAv_f7M(os1W8Ey3=qlh^L>FDbE0;cZSdUt$mtU|=cdjb?VVCil+v#&sQ7b%+=p@#+EgH)LzJQp|*4*5}(hqE4vNw zI`1F0I-47sU_TP+WK}NIfM5ks(YT%eq9~0eYHjV(HiHPM5B>gdfknsj?%l48DFfo7P1N{VPz(+c z_%Ta=Fz`w3-ZNGKOX}YIZ4+LS&WZpl+Faf!v@5}uhv@6e+Fo;Wjr~P|6tFu#GQIv7 z2(EeZ^_4&g7x#Gg*dOli_ZJQhCXP&mbm!X4;|^ffZov4ni`wDyTz)OswHc@fY>{kG zU<<Yb)FH>G6x6Oxjx7!;hm)qc&1z zT2vM?LNL1CjW_yvOmDfq7wN6rVH9WyiTEuc#(R4G-j7%xO6aD`*_X|Dlm%@djFdA0 zeJAEabxBkI7<0wPiR#bBJB4*prRtwgiPF{~sd%NVR?b=*+UUJE|M4QChtF%W@^gTw25v$roanv$wd z>>-e6z0Sx4l(AxOgPx8UFMkz%xYy%XZoVq^GAR;+pfjBV0qP1B-p}Pw>)TGBD1W4k z0|xMg_LKuEiji49v0nmWHPoR~N3*4xHBLt&EY7%l(vAQ>y({-Aj&AF-_nVl}53K*I z1(b=YN{q|~d;c2yMef&(c9?w)b}T@Q6paL5_@_q8Eh+;8$I8mePBg@kk&z4@yHERN zk2h3f96;&=E#C!o=d|3tOXqK__)JO3f2GD5KU^jlKw5Gfv+aPDNb^;kilja~{|7gU z9h}}6E-l$kO&Xr%3t(+cq;o(s^3n3RC-7WMm4aZ3`Qs&G;_MPmi=v{E$9q9}IfWxZ zS^U|v)(b4a;siUpuAUys38(-y9rU|#(b3T{sfGPzN8evvUT};+gL9TbBq&90D0sM8 z3ahfUvP9J+C8e~LEkGql>$iaL`GEo)ARcCv8@%Lv_@zERr8g1k;UBzAkTErk$bU(T zMsX#HNYd`77OSkQQ~DMK0@$M*Zzir+i(nzmPvW)Tip~3mU>{g0br*7l)>ci0;PXsq zWY%$S7dhM}B05_;`!bc@hN1GhRvl$7XMS2n)44ek#A0KKRf+_Fvo+yhkQ0z2)IOs6 z3p4)tDLEM%V7&PF_&_j@#aMbItC#mW0}IQ)j?yR|8@_b0WD|Qxmq3y*7lbi#&V{|> zR@aJ^V<0KJ5?&_f2k*u`ycGv}8m0V&d5lSYy^V^_OQ8szOLugNvp8f(ZX`^Po9~k6 zkpi5^J43P?RlkL`bH7=;s>5t0HmlUCx(|@EAiSqvY`MV6<8Nb$O=>ovzi{#= zNqIv2DEK#rd#OR6qOO_1AqHn|exBi4PPOBp}vyC zozD|~M%@QFIWl2iY9S$LY?3#I4i@cJGqjGDHdeJd-NFnEvPw$eOZrS64h8k}@NnO3 zx5mFRM*MC0-Q{(k_Z#(Lx2_PuhH^Nh2pb!2lsQC-mKT+8cYi6nBaDUA#XJdi2fly4 z{R6yTTrK^TZxTJfPo-(bHxIVxeGFnRQfQjG9riq2K;id)uLn{9wRCjO!bW{=1LJ1~ zV*%6>5X1m}#mEw;AWxJU4;Oc$h;@@2hQVf!SmKdbcY47#L~TD|kl#2B+C>byAF^}? zg?fwtmI{>2z~JcO=m>E~jfI8Y^Dp`q>K@ot9IYl#_3~`DW=D^5Mj`R&DchE&YHC81 zTkf9!9;l9j#2Ap7%-Y^AP^GV@tc`&|fJpP}B*Yq2+&*8vCiyJ z>fgN*Wj_o<{4ry0~0@zuFhWw?zJx&hHl0A-H9ruFgICi@j%q>_l2sS=s4+ z7~{aZ@6Qsa1&H6=2L(6C2GZ77wr4j*7>*b6xfAEbe*6*?^zr%m`5oW8z45OytUJ+d zIbiZ_Ep;V`#;-bN=ZQ*Abjok63NecHvkBO2vWS{Q34QrFW4^|MA!>|9nE?y&1+e-i z-Nj9#r{;Z%rCj4vl!J*KLRFzHGW6HRnuO3u_{#BzTR>2y^e`Gt1LMHd`~5Ri zUV+%1c?wq`dZ_B(5d)rM$eWs}aA5E0s(deP{iZNxZnlMHXLitRUU8iOHWOY?J#{Zb z@$0*#DY3{8<+ym;l0iY`5(8oo5+;ijCCD!nks;T9SxOvA^_tdVevZ07g6rQpZ2zQi zvy>}?A)cVC$*f?PmJ87ptXhFH7uKz4c!X6^MMUy-HYmv9N(a-C0A6{vtZ=~8GE@46 z|Cs>A{N?Gv+}s?r_;W=9CPpU%{-)j^&aQ*BB&|kE4Cg%V7H1ci2cH|LxkkfYU;o3r zTwtNtmWm$6$+xt!qrzn~X+_d>IpR&rQ%QJ)O_fiEg(S|>pIH?TArMfau04)c3^RY) z^m8&+WcehPS>@DY(l2^nU1c}UY<-_?kfu2g{E$<2dJt%&f}RhDDUHdEU+Ske#SEll z#HJ^+1mFp>IjfLl;Nko8CAK~WWquL?!O>+<;P?Zw4CrWS0i?0IxyF4vBoeTSr0Vl` zoUfcS)Fda~#=}0J-U3rG-OJ12p>sb84ZIt?QR+tPg}yaxeLw8j3l)WE_)751YEnMRCi1{ zDF8ihOBtiA55+&D5)1N~j~#&P6!y9-0?5O%-$a;%)@EkvI&$d}0`Kg0{#r93yj>i& zq;%fj*%7Vf+7apn_ycgz0|_cQIUa?SR0BT{*XWt#7Qf8T{yOps3p`i-T2kkoDehv^ z9$@+>j)=i%L>=@;r1_0MQhpF^=V?MONUNU7)OxFyWOX?W0s9z_sw8|GM@Wt;q`%V- z+1r-cv^f2!P)|kV27^XN0m4cJzC|Gz&4inWD1C^Cj)chJ{S@ZDw)tB)gU+uZcPKe_ zj#3c5(zkpj(YjCE|NTaHMS9X#9BfZ9SmhtTn>VV=1xpOj*fYxxTwol{!er7&ORQq zb{kC5EW1QMh$E#8m4A4LQqtDuy>PlFEQ~hAP)5}Tk>}8zOo4Zy2Vq@JE|N>H|4d5%U3?(UTC zkP->$?v(B>>5%RQk?wB3#eMH*Kl}andH733F3xkU8Dow)#%o5~-iuvg%dW02dfg_m z-6lE+FDi_m0?r(K53iN=jI3xt>?Wsfc9^EJu{jDwC+CBJgoLC}YZzpZB=@WWHG24@ zNd@s$kMC07I_c(aPKALpYrL_D0}TxZ$0r;1e#sLozTzW%L2K=0FIxeK{k`4EyWpT8 zWFj6dY3biRZqMGP+dwTdpddPW12_?3(Q1j|RkftvNEkD0(t9BvqT*T*Dso45>Urx{ zeR1G_2!5sF)w6Z5y| z8x^H_8qx>jQrw!a-k{#vKa6BhDlsSUOr#Af~szP`J{V(Mju)*5_XdxOipY1FK~-ziKcQoL>(LLMIX zz+LlbwcXaz5)qP$I&@rP;Rkh))FS#DD8{1$h6^xX&&Lx}nyEc#OTtP}F3y`8+8s2d zD0~dN1HEH^*g2BcX~dOt9idebmw~U{6nxjs!2))ru}c7=mta^UhuVN}fw$h)pr_dS^SUDAJ}Z>AdDBgntbtqfdLXykgMww20FUeHEUv8n%B@wDcffoV#uY534u9n>aj&_ zL7g#}-LiPo?M=ORGrEN+m*<@im5dyu;bx!eMmp}NMWbKWeA=%sMWw><{yl60&c*wT z!EfJyOAq(OB`hRT!THwj>WYdGAiRc*+~(Ey<^GCb%%t}_p>%IYmtg+^fPp3o+S+)4 zW8lNhsWv7YsM$*!_aE)jd#uRzfb~2v4vfxmg}*q%4Rj*d=l78}HcGUHy+(MpNv=xKTu@t`n^9Imf?RH1>+gdO zyBB!CMR*unpQ-S;=7_xU?->29BBY)AU0`{HnXL7`(?VE0dw8G;&_rP_S7L-KP$PeZ z{(6_kPhWP^Fz?zpZ!lrGu)r0?87@UKwZ>;b)jxHG;nZ0QKp4jC?%>EjDAjzcS8=2Z zugnTk0TM0uCaUNDe#TQWa=X3&*VL)L`JHn>gtpTS3+lI1}fuGKb_6gjYVk1y~Zxm19t+WC=e|&uGb2e43o=(Yf{3T6)DyYKHnVKOt6@cjKNgU@SFcqbkkHvV|1HNDj= z&-0CdGIk;%CMRijPBSNJ(yI=ZY+91$-A!56OcfRXw%vJefT|6ehOf6A3TOM;txSP8 z^J&Ae2tz5o)}eOn85j;zd?Cjv*J!jzDM?ANd$3J$=i8$>t%Cyt;>dNL-$~;gf!j5g z%hLm!TOv!C^PAEx(Fp!9?ZW?H0aj}5gl|f~BKfTSZw(!t^ufWOQBh-H%$LQ$!NGA7 zwnYS{PC)BHgTbdWndRc%Z9(?vjeFOX<1c_BJ1N8R<-+zUYk9@L_8(JIEy=EP;w2>@ zRL^lKAKnOhOz*bP=~4a-`&Sx?+`>7Z*Xii*%swS7rZlm9%PRUG0jv+9qfHAEP{cAc z$lb}2QBCLhnfX~B=5ILh_crDNN`9SfwK=qyJ~$)>w_klmk;b#J*$w$!=ywM@&FPH_ zy(o`|&Ji-RrSv&gP}ysLAxykKMnL-~g5z;;jYT-m!+{k|RrZqHpSar6O$NrDMcP?~ z87;A7Hj$>RMgl?vC?o~6D6I88ap?W;tWKR-7f8Qu;@tww&CEhVz8@?%U+46#mULS( zba!;D(Z~Ebwq_vKzPPv~!5rms**Rl18OEV6_ZXw~eFfjnX5%VX2-U3HPS^f;4Wx26 zy5VIrUhx+bvN&Lf+4nPbMG-4r)5@uPd~$nQkCdAoBc`01PR|s}!s;ierkJ890|Rgk zmQoja;qx}G-7zgu6bwIB$`6CEejFTX;jh93nlHmBAb%s3TkO}Ldl(Bfti^+2x@yMY z;sWOqp{Zb?LSb0Vcqsv&Cbn;5Z;7(+?@Y9%UM&@R7{O{C9~Z+7W`;2_ z=m;?>l4^*+{G(eBkXI4X_BVooa4sr7{%7MeBZWr=I4;=uB&Pff4v!-My}WuQm~~pB z-E1?yAsqcSde#=z1NWW0DdMDot!=&6eZ=pSdK#UohWw^pbM#ZKu@i6Sr%yP;ys}fl zYS3QG>A}Grc8KVZ2X5DNyu7;Z?!91RG;_B5a~;@ADbZ{`GKF9B^<7xt{OEhU+)O@V zHpDjEj*Qij$cs(Jg@N~Aj|&<*F(pziFhf73^zKtYF-bB)Nk5~vcMHXXI*gNk z50MISfMfx~O4{8Ni^bPSz`4T8iY9@8#i)J6E(nhwB68w}(SX&{_vBB-TLhhX%k+rUEarboVy-Cf>A?Y|Ea*H21~Qr(p0MHJyJ7wSG!tOS<9D`m73mn-&!Pd4 znVoQmOgJQ3ZC2`9TB&K=`W^YXxvrPr?tx=5AI9$PZl$NCl;r(hBRiLBtSUQK*R%GT z<8o7NS(&IhPLl5^4izPNueBG|%DorbofcH#&^f0w@YBy zl0{p)rmXErgRDV=k6~f>rTW=2(u|SYfMQy}{JNAG0~%(LTi3Wi^yjVLTmw^=+4;HQ zes+F-kTa19fC`2Z=$e{!-R_T;ar;=dOmwGMl3k@Dda*Gu;NamEym@5PKl`nkPwu*y zFtV~jLL&WI>F$Oh+&-AE9&IlpMOLPy{ve3$y|UCOhYX;|ra!ylSuz;c7cGL#2TWZ* z!MLscLy`GAMd|PEggUJm7JOspWo1}cSZ$uSgNGO91=2D+C`8dvuHzd$SX)~$WYSRA zK%MIPVEMJr=h;$S6sXHzJz=!8zrkLt-Elg%hi8>I?&>nU zDw$7MrmttNr_pW_Qj$6VbYAeY0r8Ca7ISn)rAFsRcV8dAv+d3Gu3#VN%I12St{DtFT? zJuvM3BY%0N1e{9?A;_0?SaMB0ol6^t3%(Kuq;a5SFj{8%%p3|NC;Gf4V}0p1caJ{& z;IgBC6dkl*$gZ>r3gvKe3b*z$cH=)L@uBU}+0^#c(d|rB3%UVgc8(*^5B90t9`V2<=7Gax<-EGSJ|I9G0--W7ekXi?wtrcXuaZWip<u50?}Xoc*0KUKqfK+HA&!RJV->`_%_Yuop4Kq~CD3|; zqKPkT0_#T=GyUOi2;{HL-5QdSwsDs?q=Lt*Dp)U?!MCqfy_!&EkFTSkJ%9~P+K zT|I8f(U>|cvhnfP3T-X+d%CbHf-BZw35G^`8EqtC%x*?dYK{C4qT#iZeSd}5H0H@| zpEl7cE*j;>#%jLyNmDYuF)L7CNbK+8PfxFr4-WYJ_pd>7tvq!>&8JVzDj^xrXM#tz zOUlrAd6WVPkepa_YH-gr0cStrO<3V2L`BKDsAI$-2A`+(G6n5M_dyUwVm(k+R76CA zyLq@}BI4y$;K?E(89R)Mi(78chsHM&1MF`ADQ#@%=(sR3GT{ME!qp+7o3Be&G1C4| z1c5G!GK+}OpMmlDSsklx1I&e)#g;7VabH=*n+@WlF^-F&@62H@12LrmJ>{FO>9cd0 z{gC{G$%ou*#p0~pY|PorIMxX%TC!U|r}3E?XF%at@Jb|?%R+RXJ%15T=a?^9H1ok2 zi}~<-${hTQ_vaNBe+Nk=5*Qm0j~9f;Rg`Z2mu@e{rNZTaBFMpY>7JF!&Yrs#K0N@K9_r# zjBpDT_BrLpA8yc9H8nlJ{R2&n*xzFe;w{^=eFQzGgt?LPR|W6H@Q-Umfv~f+zNKbg zsywgdQ8S22u!gyyUc+%8nY-`o6|!k9GEl7tBR~C7&$wH|-F7!aZ$&=QPfOo|kaze{ zb_{g$(t#7o&mTD=?cV1m7lrcAE+63*R=%h%wKySi?t?h8103ouL5PxlcxXZx?H$af z8Y-X~;uD7=-o7wX5zE(>tPAFQNK0T5aMU?YfX@thDXn(C5!CIyX4ybjh?SqyD>?5f ziSCBw=xKhs>A`pVWaB16I@$F~r0@H?$>os-?RNVv^FO`M^t>NIur~)uzP5T%a=9j+ zgxElwt#IBZVI*6wG%zkMw&{(?dy;<2PP1q#ctpg&^?DQ)ti7LGy6x2rkjbRhUHRIs zsqsW=*kF^+Oi-JQukT1vE46t4M|;xDXh23JNGSTtuh6s3P&Qw`aHV#3b|A(rSr?I> z)RP$9s)GA|*B`F{fHo;P*=Dg#TUK_oL$4p0bprQTC}^lCb#%l)e%wlrsbSMFr(e&la3@=Yla3c61W~Y?OE9wPfqj>mZtC= z(&r=&4@=3ldt4v!6P>B4sROIU=P!t`lLUZ9_^Q_;w1MWyWPEl+_d=X72y2$L=J7M+ zaB6c(4In8tp#*n&q7VzPTB#lH?cH6&+SLPG)PITF{((LDTs-SaXyA>ZkL9akIA4R) zNSeTa@bJ9S(i|V#r#Qu3?4d`Lj?PZ|owd$+ldYkW_{RfdaxOL;kU*}7jyAD6eb}Fk zv?0i4(ly#Vlag`&pme_wYa~gi-UAz?g>!yAIGTl?f=ZN6pLTcOf#?pHNEYU#GsY)w zYm;3%iUtaALAllLty|N(*7;SRct_chGs;4OR!Z7sisBb;NLrepT2pQ12({%~`;%Mg!?n?Nr`NITMVnRykdBZnVyLFD| zRB00l^jD#EwJtQAccB5{!t&8=ASDP+2VT)1P*Zit z%dId!_lx$uLr8!=e?@gQp^Vl5xUN@r1L!E#RQnpD=5R#SDuR~Y>F%+Y>@6Rg8{|cm z^>$&@n_6XY9Cmbqb{WL0*A<#=^QtPVI;hvsHDDqDPjt6LliEVFM7HPLp-S6%N^GQ< zYdhhbCSS@o(yJ5aU66A{A}_us9$o;6Am9&*TXtj!0fiNiWX2`hL!O7_kXk}dOi5}p zpDz2(?zUJ7F!{BW{mNL~ZF%h+NoGUqPc-2>psL(< z-UE#$m|lWbZIovqse<VDo$Y_SbC`#|xfI|K z$?tDes^ zN=0vMh|neg=4+5Bt~Ac%e0x?FB+LmI6VH@u273bWZDzN@DbQwsm*t2i4>a&s+>0E$L>>FB!qoI7c zaNe>IROgnH2hJql;|O!Cowf*0!0t6>4Lj=nnPFoUvwi`af$vFNZ8G3Lhe38npsCK9 zA??$*jMPM?8}=8_{L0O$Dx3EFI^{?DmE&vE=k_E?DJ2V*!G=tJ_u<1kbcPv z3ol;E{{Gs{Aw9&!mLb|qfC|Us{g9ZDAzS8Dp2LJw<#;hC56SOWH!~v`i~@@DB=v*D zj8VWZzq~^zdv?B-tce(rVk~BpP^n*-)jsfFCU@^q2++|ncgt7) z2A%K;7@SFudT6qh>AOxZE^3O4|5!?j6sU&hfLl@0l0eu?f?r*8YUL5zs=^_T7%;$R3&*#AZg&7irpDmf%K}nMk3KL zcyy$J9Ol@Ixq)F#l?ZrOmfT*$mJ$LxH+^h{0#4 zD4!zg`eznU?+WL1QQa`$tmJ z$w`ItvonC;0p`*5;p2N70AK6b+K%$4Z2+U!CG*JwfXV@FDRD_j5)OvGiMPlYYAPZ@ z&Z>>7^t$i+xet-js(Bd-UBD*YiL zz~K?#c@BTY{@qzNLg_2;Kt(a z4&gz!jevl}>a|v&K83K9JO!Ka9N9lmd)-M(N;=Kq@K$c^+0UBgiRmQ{k(RIg5#}k4 zOl7anYKkh=Y(eF{zjR7ow9R#e#U8<@($6I8?&(a9(oRuc8dHsz6^Y$9;ERad%gfgmZ@yLS#ScZ`X1cnJD7(E?x9}k& zIoKH!Z`uGPIP&9f5nN0M(E+{wABYZ@-cn4F79ne1#$c@vQdnimkM#*qpC>y`aK#7Omc>dJL zR`?#!*GIj-c}9kTDimCCgSOG%3EjN!p)ozoDX-o?o)0B|4CI(sQ-{it2rEt|h2eOP zbE*JmJztY=$DlOqmi*QU)~;l+qKwYET1f;MEFeTR_rLk&@QYaf=K!~`bOilHA-{yT zmzN@5!+5TiFlD;q^$(+upgmn9mOZwe7Ks38I{Ci%$G;f1FPj~mp=~*}dmx!$F@vPPXAt4DJoeXg8(9mcqE0bw`h5-1>Y^mnG zq2W^*9zbE*tZvUT3;c}qqn8@i<#ps`_N(nWq4d}34#|j7nq7|d^^NBn4Mn$$1(A@x zhli(KA0K;~_YNmnl~`RLuOgzLWLTScA1+LP$`J(?EcJC|S3k;+fkE*Wy_)NOejgz9 zwC|5UIrWR~=-(_fH32hqfVTFIcdj4j?jLM8Y>yB)pojPbwI>zMwtC>PS|PoG|MBCt z?bXA+-57OFR-;!RzVQp`6ueb^xPOpSA0Hn)O)fiM4Uhk4pJ8fhs`5{7j--yx^@wi6 z$=J4EAw{M5^orL#opuuv@~2Po4g16MUjXMI2p0$RnY5ZVfT2A+Dm%B*tlJ0$3HUdm zAt4VbC<7sYlro^0zc9aGv(ge87Y9slq@^DEq>yvNZ-a&4td{G-M@$Dd2c%$52xMg` zJOQMr4ZHJ=2f=IXhDk#LVti$MWn~5#8K_B9Q0Rzw;5}?aMf%>3qDL#a{Z&t2E$d7@ zD0z0)`F9OS7%Uo{Z$k|tF#lZr)0gQG6BA>aoJ`2>YBz!}2>?s-0_jXcFCrc#d3k<8 zAAo%DIv<#+s9+VY0_73i7CXPRG;W{=JTtN~KYt6J{e2XwTzdpEvgK?U*t75}q|<5H zyx#%vcG%P5YvWfaD6)Ed9qnmhbN~VD_12EWf5&!I-s#5A-&yv{)Wk$!`O(LmfYqz1CHM9fU7p9U+lx(&n6Xiug9X)$P{VeHY>I=C<4hX|bNDi^u z%=vzIy1(2vwJ|yJ{wty2P}PvACSf{dIT9v=Qgx8D1)!w*_hQ^Sk!4%9sFq?s35++uuv`L^Hh z{VWb|9b;p1PM1%pw-0XTO%_U(M22iuOE>F+%u^wy;lF`%S|_^0-^0UJr=fUY++A5! zDR3TyF`CAOavLZyJ~aj;9p{JNF%1q5s&KK(Br*_s-3rLoI}RB1aoMNCFJyFXXaJW$!Ss(Tj7>&QFa^O#`5n-nqfM7mkk;@u>o8 zW{39P-d;bE`y!CMThjvvoRt;q#eE2rP*Y=nz(r!wOMfF~nZHZp?afm?Z$ev2OURZL zeDFfc?iTqUS~lNH766pC3cK~}{t&y5u4a{Wm0ni|@8EcILeYutx!YWhpFXSx0~3jH zBA!G|HQ$I@4GYW%)|(6S`nnlb;Or|*+T#LZK4O?JgQc}4A{^YoVm*DgqP+S$DPbuo zCCf~#8^L4HTOrrfmzB|_#wR4)es<-{vgiSu4ZFL0BPm=c?t8@MxOjN{0+YQ`Sr#6y zoOXcVMn_M*T5szkB{lqxjSWG01z=}r{(cyKg-ui$pt8OBJvs`&N+(OrePZiF5I~$U zadHWi6vrm{fH5{-sw3v`5L%YR$y=C&Lb7Ur?B{n~%itKWlch$k*KUI5sfiOAlC$#>6H%0;u!m>y0Ef{jadMN-jOGp?TsBl1_H_4vNtc;ds1|7N(~g zJ!2*3xno3(|3z zjU6GqEIr~N1-&fVMGC8nn*+#mlX7Rc&)XX;t5{fAprN6yeE@}xjH=={Hq*fTadCa_G15Z z1xMxn|2mPR!d#)g?*a>>R-s?#K-Fx7cE1O z31CVwb|Uu+6ueFQyYSZKMMdUyN%P*OrXV=Q#!eBE?LDqR*3{3I>tx3(ekyp+%$!@C zs-~9M%+4jaT&kvDUR;1;F~4b9v#=1r=OO4JBPIoQD@|AzFZ>Z@x5wGJ0r`<9kG zCvwF>Sq{NH+SL`H;C4=i;|+F43=Mg+RiSZ_Vo$(UC~+KE)x~?4@-;YE(D+l47dtqI z09CH2u6|@uo$Y!#$6NIfAkt^Z0OtSt2T{Jd%cJG{gQRL8tRXDiEyuukBNXxtZAf=q zL)BknvBel*p!{?;%T0l-zkmZO!rspw;@l58*+{|2#?f~Vx932LD>LjFV6QC?5B0XI z3Whh97Y^gIl-)prG`9Rhc1?@7aN!b|nEpuMqXG+_-}>t)^m&@hnX;+?X|Ab({W_P= zWRy|38HSPyt%}I~yx%8tix0JfSl-AeH|8xmv6Q~PzK#yz4pz3E#*VbCq@-k4Gq1&z z7)5<5{;z0tQPI~h46zbkg3#U2UhdvMX)v&`binv&yQeHfY*2!q9G}-2XxMxL`!yqU zuLl1LWp&<0 zMWMQG{>zaDB=sFfGE=9^GtZ_ zqI_Atiuxl3lyiaI#7voz&EHZ<`iXCWLv@Lj)d$h<5Z3UoZ*CO(MnxlWVGVEszkUk> zRy@*&ZrT0BL_(CGJ>1%9hj0D9UgZ`A)2Y7A*Gu{fF5yO;AC< zI_^0)<)=t5vmk=kL-@pLy^MYdRtl%${8N1UFuoMuW&r6nbD5+mE9By_# zzB+Xak>S#`jqc4()#@%WWUh^j|2~X#lh&?BlE&*ezdc+DlZBjS%XN>rmQzsNY!Yr* z-+U7#xOy;E$%v^OH=?i8>RxMo?>y#*bpE|R|7s14Xqc>S;544WP{98Is6UJo3Aj7g z?TZ}DwGkk15;Ev@++FVb2L$l1kHN1gg0bgMU^DtAIT!5Mi<89agB<`Gj?h>k5cD0d z3pA<|FcKJ~(nuN`-A;;`Tp4x)wuEEOq@uUfqr_}%O)KD-^!uqU5=e@hlHK|BA-{fo z?KcUgjgzm+3JWRd%OQe0quRgkfu@4bYWuI3eDx-V0Uw}+u}Z>`N#~vS_D?AI96}38 zcuCAEM+yN#2lDO*(nnPermWgpSPsx%YNcKuyEw1Zg%{d`A{Y#S!2q9uni^3il8_7e z^qA?+OjeUEgD-<6# zB%~0|Z{NSY#U$ZLRad7CbYY;P;v^vC1_WU$M@G}s-F$?_4z{u$k=R_Am&|9&;DNSvu%x}5aFHsWn^TC zf3?!y#*)|2u&^{mS$+(|)?#LU1ze+0Snzv^4O_Y=xB#PCx6uH$w7sxU&5EW{4=33% zTue&BC{aaSor8@Hdwy5cy*4ZB2OW;Whhmk|{L55M>pRf5@bQnsj?nj$FTZ=2gt2F* zl_0wa&%?9P_)YRj;f1Dyf%|6k*Fp|~nT6#(klXCPqot--l#|mlHiixV2-vr5+(6nt zAvjnrt0uXwt&Nb-6Oed!YAP#3$<8!x4yX51Zh>J30zrutQo6tRbWZ0(bo5&X#2t`Y zFLUvu;}mtX?Gqc@ACmV+2)M@PDB=5iwnsCr8O8~?oc-^( zT~B=5(g~N~aPK1uOjXtW1;}&{!Fs-8TZD{aVNW2wXN%_)6y*nA9MCh$7y58#&IKd0 z04@(2CT8G*pG9B+=*+^te`iiM`)ZMzSa4{cQ&GY2k(rr+_@pvJSH@=KgYIgXMTR(} zz!yzU`FH`(N&|fy-b=w{sS_|KNX6FUwIBwN6f%-f^)QM0Yo1$aauHLEVXm zL#pK_BdOw95s`pLGd(rtdjzU`TmHvr^k*|j_}AKdan$WJsBaKHf<^tizfx25flYdh zU*~HE;NN@@6%qD24v-7~_{c{A8Wn}YfN;rpy1)Nm_ZxMGi{}pKWpdj&P-ELXo;PB^ zM_JZnfN{MiEaBt@3IfX^?3w7ga&nnPMf})EZ$f(d$cdR`5NgGMuh!wgL5UDcnK#i} ze=J!j|Du?WyS~vx%&rV(@PMiqg|Z|-5`?gO-u{woX5v2sCZD#VV$9<1qylqS;>S$E ze(}cfaUnmKW;asf02a(I|RmRwI_ zAb1IZfu*#SjFTyAY`Inp_MU(C3!9|o(DH145!?}BLaqyCU zAtC*K(4hp0{Ha}v4ESc4Z!v_fS~2!&{6qT1HbExmaod}MwzVao0#heYx-m^mP8aHO zpPile2RS};!xIq^tp=lbBvKf?eORweGc<=Ukn>$fTbCkadjB438nK2Cwd~}`#XD?W z_Vt6nXQS2+Ic7$gJbVw(GdO5%a|th6LbV1 zgtvJ-x8>vnh#$JdX|k%sTgQLLmKUYLB_G0 zd7w7QNByAjW&K)E>U&(=wEr#QOo@H&&@0E-)|R8&`+LFc6OTVZuU!F#aIKXiknyxL zZkSSUMMpywn-CL#17Lw#TO$1|ZXuukZ~N4MT!zSPh5B`hT;X7~owo_?5}{9+p(F6 zjlX}>*zJFTjb*tTkdEB}55CCX400MW8+Ooi_32TuK#G!@da?2g_6>ca>~JvXo|D;p z2&t4gs5Zl+5XenG92WA>%B4`NmT6QO*&7>A0|qxmjQn@jggpcxVrgiY1JDl_mus{S zBH*Nf9ZwjgD18^ibSD#_atS6tn;RPo(%Q6EM_SpnbBpu-Fd8jJ@Uet&3!qK#V-5Nm zB6;P!Sbsd%;x@EF8fN*^GL@A>zOC-lg%)x|gojiuex`BPuf+w8<#H|l_}-$T zBC}#9&delxJ3HT|wlCYrg(`S^0>+Lwcz9u5qHuI8&vIp*fcr4qq{Gx9Z^R)cR~V4(QnDBC`l=3<% z^Gl?Cb~8BLAT?9PL~!y&qaDOW6Gy_yZlj^4RxN>W`jH%sp#5dcss9ULeqA{`ubEj| z!`dq-YEE+C4JFV5I6?4>BY&uN>qeV**Q+Qcwg_rZyQet89jvynqhVn%-4 zAG_Jh?qpIpyw(d!a>zhd>zr_TX=zOE9KnMNfLw>FbFbUo1cfB^Z>ix26PVR-(yPF7 z$Cz|**)8a)I{IdMgMx#%=T85KMl=v|I!1lx;G`Pt)OT_dZY!{C1#1wiASE^Rr`L(O|CPimiQ2ld zx-UglpBPt3ArIgD#FP%qi@(L>9mHs!^|tF;~|2suSr_Upgg z7|&~pawhg4wBUMpzJ+1Y=F5o=Wf{d5LBIH|FV}u~Eo5V9Dw{R45c;PtDlvo2g|5FR z44aEZ#x*RTq4o44&>P;xMxjoPao~Wh|s2>d%uRK=W zD-`DeiK0S1Y?{Pp-zIY`0>Bt*bv@mv%pk%C5+CEZ@=^F9b8qlw0%$yCv~kk}tTC$? z8Q0kVY}Hv@T8LpIBb?5gy|@oiC@rp~e?+)!w%(sfYj)25<+qq6Mu0y%oYGEwuvnWZ z??tQL;(AVQVI=h*EFiwrh%jF!P2jfp(T5Vt)y|A#sMNZjbkKA(y;lFxeN#nMHMkDp z5M3ERSqhSoFnk|MGJ#H7LdwCJ1LE~ywJTVb()0WGn<|U_cv>(TXm!6- zP?2AM=1iIa$#Z4pB{f7wMt*nR?Eg_Co~S?~t6EYK4Bg0Py#T{W#Z6lZs(G`<1CB2= zKdML#^W`oF)&+ytD66WffEWY`LlIGTPY;OeCPpT#Q?tMv6I&v2X($-T+xGOh9j|bo zo}Qj)$o3?H#})_D#}e`Qj4NDM^t-~iLs%vAv06V=M0TPn+;RLECpucV|Xic%+Fas zZJS8}AjuPuXeUm&K3<&V@J4Ma*J&Fb8V&Y*&o5>pR`i`6!94-PkKacDuFa`cBXS z0vTR_FxLda&xhnEjXc=h@(ub5`aI_-O?l+t?Yp)Z_c+k`vM zhd`_XE=mtxZfQAB%#I#67o(u&@kD@fVq;|^IlCteOKgnK|0|6DM-rI_$77G?7@DDX z33{+A^nHwMTra=s*`7U^YzHrFx`*c$yy>2NG;`E+-@T0=K{EkBDo({g-(v@rXO4+;(BKFIu6H4OkMgkP4m51#u=@eE0b~-xm zkyH+s30x`)5m(N?4Bt)L78^8$V)s^~QMY%kYV(KYo4IAQ9&aC~eNW54D)hO^|1e{{Q_!M#I6n z^vj5g%cFN_$a1PMQ>9dsoU|3!tJUUkp}g%50SbjsC`eT30-)_W%T2FQ2=~B#Qy(?s z8Oi_sjM)vvQlr_;k6NGEV3J!{KIfv6yWgB-0l5h<@2@kT%x`u++_-Yk$prB|Ek0Q# zoF z)<1ZXs|EEN4fE}TzD>16P5$Fsb+yl%G0?CK|GXyflMqD^x!*aax^$9G5Ika7 zu%|;Ngvh@K4Ry0izNqGKp%OFrk?^!PHVWx2-ZJUi&~%>APff+Dqv;jWGBRLDa*?)J zuS^B32Bt@OM`z8Ljp)Vs<{I?5;eEy0>>ry1vWNxqe~e=Op9e0NtZL73({Ay_StJD) zQG0TBJvf~wf2y36B5rU*yD5U`Sv&aZ5XecYmg;a@ZXulq_p2j#iEPF|{GT5f7qyBm{Vmf%&$U1kUtP0ndU_r@+iRpM&;2So5o8HstKAV+Ri)(0c@lc^WKlCdk zS8P~O9&0&>5C8N2&+H+x?yodvFB3y39M_oBuu*82&c6 z|9-hYu`UdSFWNs>^W}EZL;d$FiTkl35%m7^SE!p||L60=oeBTieRs)%_JM*1z%QcIUI#m8Z)Iq=w{dnR=^W zii(X*8ER5&tmkoO*B|oH)Q4@YYq~UvIGWSSuuxB%Ojw-yjJf}O@f3VBdZoi*FAsMU zONnkU5zw&EL3MlD_}kb{1CPtfp6SpNsVufxy}7LByUK$OQ;8be`R{YjV3y6`etmgw zJywD6*GoDN^3B2C#>3)Z?IgKRu(RGK!PyAS|Gpw(TO*efKk!YGUfpA=2D`JBGjABgTb&M)cThWqcy;6arikL0Qu zuU|^eYO$R+B$rKDphlD2bl`xIh@}_@T%D<@8W0rlIUIx<9sUBMDyz+cx_RY|y$Uk~t{rALfe#sR3@xDS@Oss!ogibMk`0FY9V8V4k zV$@hvdn6$&q*S!i;e_#cwr-$+zMUO|^yHWA-_V!$+ZjI0R&ytY@r1X;|L1>$Uf(!K zjg7TxM8g5k6^l%TDz4)4?99>5PVHj993+K>#xJHyju(N5*tjh2q&8g6d;PazD~*=e zR=|#CG1J>?s2u9nk^lc>wQO+cTVJElVs}L0AXhS+f?%z$*(MMOP-#x5eSrp`^dgPR zmS3yk%^219P%jwdS?&E8bjam=aXdTv3!Nl`4m`=t7@-0~mDVohhJSAVZOzS3i;mM6 zQyQ&T@C|R>OO?aO;&X?AK^4LN(UHzl7ig^rz=AlrEdKSSu?ilK*Fr8{X-9_*@8iv% zup4aNhZDo%cv{enS?G60{ga^1cI}U!9(f*Lg$Qr__OZG+_vm_m_V4w0CIB~<^LDiH z9GhM<+m9qk?jv*UtG6c%RC_y>hNxoF;_8660rnqI2-*4m%D(b`dfQe!NLxuKxIU{6la{D-D6` zx&B<8wW!oEI@|%To!!sX+(>Qwe+v?*u~x|faG~v;?91d+j=AURERQb+R+A+3 zEG@r$3;e2CXU1WAbwH&IwPyVR?DW0zfnN88cpiu$d#nPq1>vx}i`|?A_;<3Z;6d%h zQl4Ye?9Au9Zu)iC0$!YF7CQ$=x!Tozr(kfs&7zFK)!{rP2E!3*ES0jS=ff{yNw7n< zRw_|+rxu9R>_tvRH8|Z9cw7g0a$PP3rCh9Xi}P3DpNmfUIZq-U4wv<&KF;#~V#j0< z+4E_`Sgsu;x0Jzc8=wz7pz>agNqpMLieZ+V!&Yfa_3ZC>ttUG`O{ zSi9QYZMCYKRt10&;J}h7P`nj@V%nR1`_04EPM@5-pq`fbOdTYW+X;KS3N~EbDx*;N zKYhPbS+=1Q`UEUy3PDspd4=%Pfp!2P?w@MGlhwB&?dGtsf@^)&Hi&@Bjgyom&H!7S z>d&9)pTg)&=iX?W)-PdAg!r3f{~EtU-b880qNYh)Z0yH`p~J&)YHQ#8-G&BNFf7#6 zT&(B77PZj8-Q3;VFc9BJpr2HxB0a^FXJ5&hsXY-Y{Q7b?HqRlx(phM43ceJ7-x=KY zn4{X->9rZ%Hol5R5=G*(a8iEapyc2HbS5&+x48#L+#UcOONvI2I42bn5nlP?>;Q$v zb+>45US$69?=n|?-QnEr;_=V``yP0tCEC>zqZuknN*CYNtAUn**o0Dvn*K_vTtf~0 zf3Sf0H+!=w93CTaQDY$?&8TjFwn|wKxuP$?r}%8V;K(iv27L|=gJ-J=&Ie=ZL$uu7 zUNfm~z;X&mXo3|bn=typr+g_9gopwK>`%tlv3{R>3UP68vRI7ib^a0-%9&vP z(8^Z?zOgZ`)C+PZM*F6mUdJEcoPTDp-g>6C8ikS;0d zmTp8qy1N^sTbeW3d;jM-=gKR;XR+3dF~(Z~N#9Wp=lR*#s;!epW-AQf>5%zc=T75j zV%}_XE{4SydR!kP`@2}H`=1Luj;50Rl62l*x*F)f1J`jXpc}@HQ?Sp1Y-4iKPvG578k@9C&bTY}R>R~qS+Mf7@g}n2{wp3C zVL++2<*;+eJJ&8PJy-h&o!lBxK_NOXK+>v?lU9fMhfy%nDbzNX;RH4_X2SlOC^RXn z13T8A?LVJ4_FgeCFko1Ue~CePvKF>j+uM^e9k}US0eTKnH?q^gfGkl;l}J=>#n0cK zcC+FrLc~dV{_ z1fF2@XUE&~L0*q^dZxtJ2N`khgGQBOF|bOi7D-*G_g6#sZL zMwY>1U9I^rob6BQCoU!yll6AKz=eU{SI_<9U2QSdg&;${^bvfjDIOFP?K!&nX$iQd&eCG2m920Qv zepL_o^pvq#+8ag4tjh{WfK*l1vjN`@7OoB`m0A>Re7rIj>ZI2c+~2kIzk{h3{}|7 z6iR4JZ}*e2nv4AZdo9Q>m4GBZ+owIxVFOCc#Iv)o@MxdQy)*v)D#v^oyyv!r#Ke46 zdQ)}+ysWQZ#h^?!&iF+YFVkNA+A05;ROWA@I^eiJtAz4j2N6mDF3eB~>F&>OlD4)? zY_*mfa*XnkNd4Abfa&=2Kw^`7owoO3cbKcqf<3oRzSwQ=61{4f2$V*pUZp`LGoCNQ zAvdCGx$^eWpj@g_M~F@y@@HF``XT5P~$F8JEi(h}d9 z>a-imrIt4icwsT`%*@SOwnyvpT{saf>Nwrr$~(nrW8(9A&G|!rAw8XZHaniH559?j zEAV;#1^6mfvn8+uU(fxdsY@Z9njC(7$P^A}9-Pg@WzxsQx-eP1Ch>v<7=`+rdgYVa zBz4q9^e{`<`1q_Y@8?~1as%G-LG(Qd!r^Wu2%_oEj0Bu2r9%u`j{W4pVT=DwD-dY? z;#jaTauvS`wYe}bQc@;#%RTydJhyu@sF!tGeh^ZFdI-zfns?gS-U1^UZRf{3)b2Q( zp76QxJjt1|4-{l%>n9yCaeZ6|Gq6$kXml#dD(J*~@5tSbSM-XFdNz6lNgYn~1p!{- z^3>I%px1nLvM80W{Oo4 z^VVsGIF#V;Q3@d9qu!a(9zs#Idi9jll`&)W55{ET?hd?Vy6r>1C`P@OYZQeQPQ~l(50BDk z&4<#4Ket7PN864S(b1k-O_!u;{gqaH#H`gmN*p7~&XoL`S1#-(hZM zMf6k(Nl9LK-Mt;K2+1CnM0vygX)l&WF^ig#5}QuBdvP!#t8-;V$bGZ|)D^&$5c@Cf z@zxeX+H?*0`V9I>ir=Ll49vOf|pRpE|RldNA2cb<2>Zthhq0`$Q>? z*!?fNJtE`(J7r=6UTPgAh`s*(>5h#R!ZdPZ$gQ>QKZqSgl?U*B4E^OV7AAOs{}<=^fYX_Ln_HZ^r#N6`dd5~QZe1Y>ftCU$(s zsz$t!4Q|H!w?$_|e`zIG{Bk_ah01O4S@~%gVb=wn(V-m7SqV7k%Os9nGRijmQ za2RArVc<1==!nN;W|3*lhwdrOCs>xP(P1tdjq+3%`Vblp!}1F z>%#KysKN;B4TOMJf;{$=$xc7Xy!;CdcPQ#eBGgI=*<>t&)~6U7Z6-nYk9Of&KJRrV z>R@1I3YH(a&Sn^vOaD%^BW1l0Cm?in_l-IRz#kxuK>6!Z<~AAaA?hWx|Jy=^(!ES^ za$^XdM-!^a#=7%xe3y-(EI!U*qPAKNqPbrk&6jJ$FxS^}V{mXCpC9zD%rbRW&Ih@` z8=V+1Vd?xTZWJM%vfvVnQewn)g8p_(BXJc$`p<8tfW{zzl$z}l*2apCBG(_)X^aEb zZnR%C#{A8Pu!BFCUS_d8Xgr0IVzM-%O3xia8W8=S^`2+jHu0Dt44^nJ7?(@$shkeE*!*Xj>dlu!AdFs}x^lC$! z_#E?AmVCTaJ-rJH)|z5h)}mrj9=z%y8Fp&!~e!MP2OmXTB5?Q~54z z#lN}#n=aG7tmBlfhc)woPNJmXd8^um@83-@sz;)uF{_JOqXxYI#iX*}kHN9QX`O?X zTAg=1x&ED$B`4rM%v#ktHtAAg65!N&x%U(qe}PLFFjf)3e=4t%k&V9fWDxjl$Gvm#dbr*Zg=b6dAx-&&{n|tjdVFtK$W0 zu_^KIRFDz=WgpuGYayRn$7Q>K*-V#4NpeL{SE=2oZN6-xg-RJ?E2s96UFpn_L8EH1 zvMAK5qiKNxKJfmcihsY&Ze1(~H<6k+O3sX>GJ!|M_V3x23;(hMkRm@cSu?WR$^D3` zb5VEYmY)QCH#D?slPoDqgE>B4K7}lS=P7WkD-Vt-otybY0}d@tnvy*g`)Nzv(Jx5!5z}15V?SA#nDF0w zMl|x}PK$*y&Y8kS3R19DATzQ)B3vd_I+i3R+cmt0F zE*kij=p$`^?xZa;Gnh(MF_aDrv)q*GQSuF#h)Pcf>i@M;i2s~93@`$hu1(F&vHi`- z^G)s0_g5oov}^fF^xN$7K_|Vs`{O5T>v2%68$ERDAJ8tKbl0(f)w~RcGY|lwA4>Iwtu)r8^r0*<`u7)u{sDH9j8vi1Q+)@E4eVF|L)n;F4{^E55;;`gC(XO1 z-uY?wy-r-8Pl-a zmjkU{e$R3D0i}Bn@#6^5%(pdht`ZF@vE)x-a5F>~p9UTx#IyhX@3B1ppmk0uZxEPR zt6@GgynbY^MkYyQ+7xu%!a%6CnfIFCzrtpSyy9d%3&8=y%6bydFDS>djug4c!Y8wi zR1f;}Q2*fwm;ba~*_PUQWW*kP)E`re;Oz3z3Au(&Ec4#C#5AyUZ8CNX$BXDZsRv`}a5 zOCzwfR{`d*z4A(f;-v2mN@p1`sbOz13Z>^Y$!& zTyCT?g$g+0JKr^50IPi(g$!2LA$*nowTj>`Us`~*SW8Q~3^c%FC8*;;VY_$^)hdX{ zh{K@HSZm9uJFPWRtWx*|g#?66IT#oiaP%vysFbRf!=cvMtqQ@m-Fu0A^1M$Vz~OWp zNT9R(U=;$4IR75a`rg-<1NDRNy~h)a)A7V~j2;ZkZ69Ze&ZaRQq^Jr`F+jlnzC<1yPS`%X{^xKg38_Z)~gPv>M)uBL}Sx|bl zlbIxSJL27ED9!R2m>Z45vHIq$wbBNf8FOj^ixSr({zzgr zqlMbyB=aZ3xoRgs;E_FELIa{IT3xi@(&oQ1q9^TSAz4deAB~Nnd#dWIb58eT{?R-%QHDy)6_e#Vc33J~KVWk~LCO(uK+P7b zM5IL9t+eX3>}Zhs43A!ICd)?4&uauT&$@aVb z(#_w|-iOLBi@5LprOr&*x~cAJD>VjKT z4SaO!o5~IlQmxsfoKGhH#d(CsNZ9A$ExzmFYNdLq^>hj1!pih?;vt{oR^K%VKk(4h z$(Nb_Cg@T7X})W(PCOMT+1;)dxa{mIqe=Lp@!53g69Em|_+ZAS>aXu}`ZxfflbCb@ zSPLu(nPtf1MBFdOd_X5C_;)lcA*j)h9`b0RE>XIhREZ3gA6FaW^(+0UVLCFpC_9)C z2p*f1O<=JGk4_Tqfv+_$3{g`3`NAv%q8-b}eBtlvYO~Aa@89M(RfgS>mV5BX72e~y zJxYkYyz}yDoVSK|&NMVMMYaBca4rWkOmu92VuYV{YdqAz5HAO6cle%{lJgyNG9b;n z0~|vl9DBaXa8uZPq)fAYXng$D@ePl7h-QB|0N-JGe~}OqV8hfcgTZBhdfhtHj=`0I z@1x7*T=n)~lBi93f2utI4q=gf{5t^;B+To^zWa3DEDZ*J>F02IYS!!aXCIH%cJJ)# z$a-^&&dpwL`2Op|Wh^2*WQl-NKpy~76CzyK79b5N*Qwi|DQ7UhUTyOn1@raSg5eeF ztS#N-qpU=38PX77>9ftp$~-I76>HcSx-FBeD)@_TUF~63YmOr?X3m?paW5at!^jn;_&D&FsDOz~s^SiCJR2kInzweU?hM-7`3$|?Zp z%t%e0uhZcPd$*8(F^is;dwn8|L4xDG)n^!9=Q5fmNW_fPygYVB!tT5`da-l;XHx2< zD~CepxaABI@@dtZ=lH4ZeoQvHk*L~rm=I$ny3EMouYJS7Uq6Xnnh1#vR3h%fWjx(# zvEXKx1^-jcCM&8vrM+yGGIg5V+D56fsqgN1yGC@yEU8p+?L@zl+o5D;TOA(#A3a4A znGM>zdU}doN@rrH_t=chhBL-%%oV>f(pn?{9u95lCXGTQp`ausZiD4=8iPP#P>$nA zab$bP)bR zIpZx|0^7SxUME~f&$~;yr7`Q-N^$D!HslY}Aq^?$p==f$q`L_+`NbK$pN_q|WTf33jY%1(Rt%Objj_tjZ>XM4T{Z$S{6~2}-3L0QNdbSyH7RQ(@3?}1gisXn=@VeYx0@5V_ z16vxK_ti+=G!ACAI0vKaeQXe^lWcQYRCix)b%53$j7sNOn?$l~cKx%lzK-^H8@!Hg zeI!TkiSPYN8rd4pHy4TnFyM%`n{9_SSpnQ{VBRKzmbB{4p@<_p06YfiioyZ<97W8F z`Sqt3E{nc$jhTG=9qiYy@$1;$_m^yC=0ZT}vPH+{^Wb)xp!vg@PJjxSlEJqOO-=aX z3cs;S)MiTTTQ}@!WSuPDc8rtEQ!9owDwZo&v2xa~)_hlL*`!o4x$m6dM}i|>!y-e9 zz30sN5GJ8Kmv88chl%{|w6nUd0Z$1E0htc+AVU!b*rrTdrX>w-Th)blOZn_je1mXV z8Y4nr73Thfe}b??5q*;ts0ZwzfxmD*gUP9bH|o0{00)8k+f@;H!f{payCoYa8ypZz>`wDQB0qpy(7_&2BJ#O2PgPImA} zh9I)u5eU0fOy|aF2LGa$sx+*maan^DNW&E9O8`j<0edP4!9>7DD$}j=*c&snBd)eK zH}9}J5!xGe0;5{na`ifTn4KX&Mn2+GLg-mq7Tt$|bJYQq1Po!v?oeBQe-5*eeGos9 z$?x_xUPXirGoH($BWw3L1E6xN(WLz)YNgGdv&_Y+NXrIqO^#bX1OJ4zmQyuaYE0bvSOz z6Y+p>DDmX#XFtdeMqPY-d}&O#lh<$EMu@}Sv0JGs4QtooF=-DWer#JG%RnX{< z%z7#WQC8T-UBfF?K6*7~!`Z47lB`Cg?yEwIii!wRl?J|ZRqI6Dwv-c(Kmch!S-I6P3-7 z!{fR3av)f=ulya8RmJl5Ee#F)G0Af{fHhDSL8xzBDHQNtHCXglQi*^q7=*jx8tKJw&i`H@-ZgFG4^A-6e8TA77!wpsP zwGBK2-pWVMty4oIIu|!LU{Es)092>H5$U`r!>_x+keM3>mr)ZU>z^3d#|Kt#4?woe zK0-Y?wCi|;TWs_Et4?e1dg1!-WMRC46=T!AN#%H9c;GJvyOn|gg9ckYr5>fDcMt~6 zUIg`kn8LVuoV-m& zaniDuYfbqdc6R8Uw$v5qwTs8->LS;0{5*y}Y{^KlhZli-P6HBJ88&H|v{dw5)va(! z^cGwemRw%6l$gazeF8ZBrv$~S1>>xf;@P;Oh4u<3(}Y}o z8VJ(em^so2IA~X8szpD2U47^An^_uJnxD=SZ&ABK+hpKY5>70P~Tb{`jG{QB4Rl9ri={o~!$H(_6e zul^&z-@R;{xY_xS7+-X(V&yl%UtP9RQc{U@D!mV%!PU@{EQtY9Iv~{Gpx$mAFBgt} z`>CUF({J)aok>UZDTbA2jpu)}01!y)>Kc-=n#_b1{tlIh)34MIubbGEi8YFdYr|uL z6lEcYoC@l!rl)jVX$>Ga42qJO^@I8n5)y#t zwx5juqjq^3N>s=(VX8EWU@Opa0B%+1Xv6s!W_(QZgHbS$P*6}J3qag# zYGx|>2-0_xr()vcCp#gb3!YB{^+!lG);!4Il{&r?b`S{6DM6$+-)qIZ{y_bWe!4`> z)A*Lm`)}tl5s!_>^`Tb(S1LtfbF<9`tN95p8~k7as%*;dMs)S-Kc!HFDBeL3l~c7* zpJhRP<4usUkwT8(<)hyDZc_ZiguNuF&{NkaL|qCwbGts;)lxCOJV=>=Uq?HKs-Bza zfLI9zNhLonKmhbGYN(-#@1wo0hh(THi`5b|OVo?C>n-MUEe8+Fz|f+hYLty@cZ!sT zhUm3H8P2SBzOw)8kSeh_LzlQ{{xg_cxsRV*HEz z+8;4D83tfW)))mrr!-?>-Q?^RyLmI5=dVB1M0GEr6ZZ;Q0($AoAdw9*HB`FVfMCon zWCbpfCGh_GpfWkdg0)S0bmE(CN+gB6j0`(==h(@5$21Ya!RCb(JDIZ%ooj#tkmdJW zGhUr5%E^72DVqm1Bd~ou`SJ$8L4RdMKM)R;aIE7Q2+!7zHmGFRdq#yhIO3nLZ!a|G ze0mNP9WFu^!%}>v6B&}%zXR1+wxi7_{DU`_wIA``i$`LO^_QuY&iF&a zAIuiNdh^^hBe{~dI1A)gqr<~d2r(8@MRCAU!ib!LP@_@{nq^}tHG_wNAz};4F=9Xi zc%5Q&+AANMIbbuWM97n0o z+}zw~_1o@HA*ysg2QLyin2J?ps`L16zx+bY#pi!66&Da#T2K7i)tz4A3l-Ymnqf+5 z{+N?!*9wD&XU>cXldeCH(%;vY!sbNH^Oiv9{-I6cGgDNba38Bsba9q8m$gF+3K1os zf}$+KT^%^F+1g5eP3^ds%%aCl>Ja~OP3otZC4@?T+xZiEIE|ais3jX;hzhVjMXO_| z&u1){a5mn(L2>6sw9vcyv7J6s>_UII5nL5Z_UOTByJE3f|HvQx?bAZF4Z(A`6gQkO zT0|1-MTU!5rr$TMN&u$n!_!xQHJNP| z|26)dSX_E3k;R&>1oJ0gytY0Wmym$aFT(l@FIJ8kOP11}nSr4cOhJQ!@M_iLdDgU~ z5}7n+Mn-lf@_lcz#5wKqP-w?|b&%+tbjec#Np~7QFmXFm~wve!IuH9$6 zK8x9R`pI{%tliiba%i0Va;%X9yt=yIQ^_WzoOL|k-rb39%-Ae^wIaj)ID=4@PpegD zrIxcPZLJC=@A`$Cq(rS8s^2zvIotz#xlJlNQP(C>x^Mcm@%T^%!xEo2m$A|#Pjb-s!UR+>^14R|2R8-+x>U9``*Iv z)N_q;?dj#^$FZDW+g{!|YEC{kNCQM!m{}RAao$s7GKusQ3|Llb`QKHPCq%yWc1Vv| z6(?I-l4{Mz{XUgKPC@Lv^-WMHpB|tk3mqD&jhyR~QhfbR8&Lu&?L$wkHpST3Yg-9@ zGqc6{d0EOKcQbVKn3NQJaL%260v#V=xn#bwXz#ag`s(U4h%EYj+vn^;LRAcuTj8qY zX`Bp06fulceebbaar?P zxUeM#O)%Zxz2)Vb*Iq2)vO+Q)GlzFZXJ_Zg{wadY#`Pv8v-v6Oi4I9r2n7@_-#;cW zXhi2m`(YO8XPz0s3g(Ii!izVAxciQ8%Jd}yMF!bAuvq%Z-MN}ICyuGV<#Lxp1Ctxf z;&;{Pa$sg_%lwBg%XrrOu0)&5NEW4ya3G3~isGZ7pkQENpszvwVddIdr6!oZR3*B% zklmZ!4E$&Ec5v%=|C%|+VL&6{!)~>E!@CKRb^wz{x*A$WTuhu^qr~Lx6G5Zv5h2gf zX)mf7X61U3G4K}~gWhGyFvqnrGF5hWfBT7{`j$gGGuP)f*a1&BM+`PMYj@PVpzG^= zVsMd_irYWu^DDsQkc*4tM&zLRQ6(5}h>*-|Kz)=tB;?}az~<~~irt8;GVHeCtutwqdorS(UJVwK76D7Q-C3R>>kisK6oIY zBC^Su?H^-7e!46+GQ3UA?}LK&y?^`*h%sG@1;a>9aO|ID9_~+oX&>SR^!@W7>kknT z2?5W`x9eJH`Bgvakc1rXrt+=dTh7lo-u4(L+Xe@fPCD9bsV5|n_;eAf^^1SGvX-E; zJnCq$#jg9I;E`H@`=?e60OJhSO>*>$E#`{115Yu=M!<~nH4#yR&As~h$Atd#Pn(2M zgx{iKfhnqiE=-8PFtHe7ii=x0l657u{dY68ee%kVWb@4Xjwge}wsp_M7xtvo064ByVM^)~6c;sr2+%>#2}Ao)$beyqhqWRXdttu`H;;V>T=N#$6y z*Z`KL%-Xg19;wMJdMh>Nb_X-%eeMdeu8Z|{ZqClMU>JmCyi#-HC-CID)My9i8=sMJ zxv3~DI$Q6W$w!_uHtjMJ0E;ZJw49^k1^n84S@Z&I*)9R^uz~|jt2Tct;7mBE#nr2Yphuf7Cj+qSS8FX*K$`$c z?bCzGh-?GoHhUlTwlAu~Xf}aOwXa9cwbZ9iQ`GFlR+~O5UgN#-(1%VgCVY>@f4Z>!U>yAukgEA_3H81*jkZ7k-RPoUxVo<6+A9WP6s- z(fx_%^`67os;}rHx82GTnDWRyru)DB0d5mT@W#Mq;na3nI>8R2mQNE26!5sDJq%zn zk*?M+Yogl@arf{5g+7$fN$G5v;S*MW zhK5kw(|)SN71SLjg(TP$3-C7Oia}uvrE1xaa+y$9X_pTi$g?YoF%3tyo4sxVpyZ!F zmP3p<)hQ-LO0?_MaI@Kub}`aYNRUhFsdAFU-`mL6J^h}U%gU9Il50YNhKE;b5ds{EGv)GXlveeDfb&$eA)N zc|NNE!1+*pf{u6>0D&*`X)mqrAo~D4ZWZM9Ix!ioSQ9d~bsrpy5$&d_2B+ z=A3PI9E|0(ty4jeN@p7=6odrW2k(<-=51Zf6ake5;*P!G5 zX7AYGZu1przu_QJ1bxoyG1P3H$g=)=lVYUwr=m*}Hu7h?W*gzLXq&w3hUC<16t~Z+ zW6~D+qIm(fx*bm!t)H8Cn`-9!M}fv`v;EN(Y{o5&IO(F~^7UlUFbW!NZX~f7R9ny5 z7Ma8jdi+%W-z?y#z5mXE0ZV+VA{Egr1Yt}mGIsCyI2&tKdcQ2|qv%X2-xD*&_4@ic zMWEz|&rIeNwUSETxvZwfIy6^!c%~G!t$;7;>GLSIXNO1u;fy%Ajo%3Rw0^!w^8Wer z)D_H;Kn}E&a@*OlRL!qQc|o;C;-Q2$L8Vz)2J=5mfs07rE#<6u-+ko4G>35Nx{A@! z0@u=2T1oyhGnBu&^tf1@2<0piEPb6Ma|0Afj#5BM0)iT$&MRXj99C2%k=MS5hLK5r zg-DwYGNpz)8$TE*MJZ871#)Ql`E}&2MQ_A3OFz7V*WgQgRUFx3R-K~f66T-j+$oSL zJv_-r{Y_7l#KeAO3R<)s9*(^-$jCQ8fH)K}SQmD7L94-rGDp34Xb5jS@^HFjPU|;m znEW8A?$5o+pn~k;3GIa;bNNo26|QI4L9$-o*JnFA3oS3uq>^G*_g)VWbt$o&+rERr zgX6_^+?G*Qb*>(le(Na^wHyU~|G&BIJ$o;#g2gv7%(Hz`ZdR>O^{~o}zoEL4FFc|_z zK#IVC*TNpE1#$!%h{d3W06K(`ZqaRDDY1aiQ~o7?CY{e zTT{dFeM@ooxTmWvlWo)YbmNWw*vmAA7tA(cST1-EaGu6;0>i?>vK47-+yJW`Irm&2~=AZQdHl3hQaddKA<3N4`X-h@Z=8?kT$-T5R*VqVZ>{Cr}kff|% zr8T{Dv=1RR1rL;vQ|MZib5-shi3J*?USM2#v0MF62xB@%c~f_pC1Gnb{3HSwpz>Y4 zl-MRSwZAR<6G3L5^SJXZv=l6#nP0Icqv=rvb_>Rn?j_hhd* ztC*kti^q#=wb_X!`%;hQVWSe>N!OeaYD-1D>yGD$m(H6G6%n)M5_7#_@3AsI_srEU zH=EHmHa|qxgha_V#hldclcG)p;+U;NG}?%`v1dP{#$nrDP0~ zdyuOE-x%!#j2&>>PK?|1UxjUTxz^X4^pNDbi{8XR{B5(nT+CFNHIem`4HOf@iEDUB zz~(PZ&{_EX;52E`MzAqt^j=1MDd z*j&B~F7j0hc@S+%@}=axEKxvzYP7(>H+ecD;;5}^!!c#c>K$6u6+PrQLkRP ziHnN^pA}QXFXCWUZLf$Zdwv^30ps#pZYZf!EsRQ;DG!D~t!vH+7CZ0LMR{4|+bKj5 zy&p1(T3vY82Ya)%(Xq1qGhaj5M%p|s&+v*N^O3aiu#4doDnHa1=MV(bVMo+)c8_<< zFeb8#B~PwGy2;!fRBO@b2s8KP2 zgduls&W^P;b)LY*T9x7gIb`g{3zY*SOm-(+_B&>u{4Wp>4BLR>Tq#WnWr^x0pQB_! z0KX#!IHh7a5R$QfgFLH^h>2fbd>>2dcMGONmHW_(+9m0f?QWHtmHy3T zw&<8^iM$W5Z&6syY&30qe_tJ#R6Y>@f#ehIBjg4KU?^!IYKednh61)J)$-7o?!-Zg zjeEjzd!TC(w7|gZGSdtWCw=fu0%c*$83CHu)U>1mW1j=jXLE!X;@}E8sG=Y0`mI2t2$0;NJ`>j< z`cM%9S>V1y8At@VG1l9=14;nEEMAgZ@Ivgib-8_R{PL~Z9RJKn;;=b8nA1ugj4+z~ zO_(;+DX(vx-eM$71j7HdDYL;3bdsdbSQr^W3x_PBW+VH+oGkuR2Ac(KfSc*abedkR zM7;Dy z&Ho(D9b$YNJe8O7MW&p2V}S7x1rsofX(*{#DVpl|K$=}y*(Zi3U0O^@Nyz|iYP8Z- z0k7Hb{!5o~s{ukcQ9H&3y0G*yspmRMufsIYx6ZH{bJW>O%OV-Wq`$^z#>d5xC9#Hh z`zw%|nmgmsdhX~X68NzA4UzF!ydE=ha>6DzyJCowEHvz`DCKfY)s;r2b29~c0ps2-kbOINZ%&Yi)q+7x$@)^mI2Eogu{+RldA|ap{CS@_yuvLgI zFGod4|K3eWD<{?!0SXdYbq*kL%`eDj&#RjsFF#}1$oZs1>0TQW6!b#XWBxregG*^^ zIXZFXaCV4c!9>^5)+Q)DgY{dHl7=RcUJK+Q@i24aPSm_UhmCD2k0K%NLrGJk-#V1( z!GOp-(Hh`Lk_Q2cK-G$#%R|Kls~p~gNpf(pV2@Jsx^fx~-8H+aa}UEp^hv~2)_wbf zl<*rB^8&fzp8>uN`2AUbxq3gXJ>fEGW1H*`)+XDp0xT*Vmu`RK5R-raabJ}d!-`A$ zRZBtb_GyQl%2yRqEDL(zay7;px&f<4^6E8Y!|R1AzorhOAMGRoUhDs81C%4JKJH&# zq0`o21)^TC%i)Z z7>2BSFkd+*w&ro?nNYf8=Q^gK@qKq?7%o$dEhfbo>26SWQ1|zDeX&D}z^qVCMkd#V zlfYa4Q{dJVkhZWF%awoNK$7Y^Bz0L{T<;b3L&*?feg0^i8EBN^BGvWZvAFnQ?KVKCk*l2H;nu`!z{}Hjl){*^qk#`5;xhpGe&;`WEycMZEX@HyA=}j;U0$p$__dBv-b= z>`;#LfZKVo*ipXNT}`s5U0GGuUS?t|JgiJzM=3=~Y5ZGA>=sXJ#wdhHEy4!r44z7sNSuJEqH(PAU^ zqbQmUQm;!AV!dJ}JO1txnQW&lMvJ~jOxA+H5lXKu8z^m7)*U%(@rn|9yvMy?**XYn zBlrF9ee!T=@3b?tVfUxplq&T;otaP<%-$`z2eEi6uOM=D)oL}9%TiHvz4o}7k6%Xi zI0S{;dK5h~-y>ILcOK^>wF`Y6-~3$T0Xt-(mzi=_UT!FZ#GcX?*u9w+Vy59^7DC&x zqqX?Jt6xe)vguBIoS>ZlF%9WpAcIq*2r^%r&dj%DE$N$NEDOOuuLq^Qj0?+7@^PQU zT7Nb7P6A`gyT<$8^_R2~8fj#3*{trydYES;TYGYUb2}kw%cocIl2QjZ5nGrPtp4(( ztdXOu_f{p+KHtR1{Vp)8_7ik`LtClf_vg4iBAd-{@RZy<3hBu!UU9YmK0h11y*=%E zIC&Mn&Bg|kCL3$l{{;>rAd7(HqcPA=bTfj?XZB+3pn}sYJ1QJwyHO{61?xJ;Cb@j| zuppzvaNbxg)9~?pF&yjZWSQ^UOiPNWEYQkCL)C{TuM*^#2S)(p$vL1?g>>AG<~UZu zrJS0;o2T4ghFCT({T*{cfRyPXFf_$swOo}|-8)W`(P*)J`znkyivdhqVCX0de9mVh z@bEdjQykOOLh*qEPs8`IW4QUNaQp+>pgO`R>7fJl)E`@IRM>MIam(eLc zap4j!9~i!WB3JxgA6&_9UiRP?YWTldz^oW|h-^iFj3L&6#SW7%@)=U1IyDv>!y81} z@}$rHk<&kDaL$ekl;x4neS8hSP7HOz^tLMwkgsuJRp8K06Iw*&dVQIuM`e1&A)H~M zNi_;b7p#1Rs}@L_1^v%+3*c%8BC<>*6xy<3O#nM2x*f{NK^fTt*Nmt@5mUt$`jzV) z#V@1?=V)XqURSJK&cWT2oS|5dA3Age%4hbdMn?CimqudjAfMRR(ASw+ANbL`)beFV zrB_=`FGANTT=>%JpcgTaS(lKTbVss0%an>zdR!&M!sK9v$?hB?<*2VtPx}V?svBx> z$5b6zSB}jyM*FDq9lR$b|*T)Exuj**E?CkCoEo z1IlBV?^hE2GjJnlCmef>khC@H#eSj16goQL)Ym&oXODl7$ycYv4wVqAj!pc@$s8E; z!pDO7jF(ak=d^x5>SXpQszm2!bxDl3Zpn|?09IR#MMB#SpH0s@GpoH{mn%`rQAi(E zJAej|rrT(9nT*{sX+Wc9NhKP^WIKUXOKNteSG{Xc>aUC?LMRCf%Sxw;%E~W!X+#Ui zPZE4))D;li&QIcseGhFOaO`#xhyI6`>aVWoslJZ3+K%54@KAQc}4GUZjUNGRZu>Aab4Z;a_H$m;v?YaJJM`altv5!@SSw@9EV=F z#iP!o|EpT*jgD+~93t0SRQ%hG7ePf|%g+R^lo)BoC3ZFlC{N~A``W|@fo$S1mK{c{ zh#+6Nq9rGvloc{iYr))VVi*$R;9Q-qi-V*s4DN2R`W)X-XmYRIw@|d7G~cTN7TsT4#CadtZSPq_T#|)D=d-tZ)_{mZt%vRWug!EzVgAZ|v#$J-;wl z%%zPnboh13apN>HlUJ@L?>J|CTqd~dcVQ%eeI!U z4;a#UOTHJvf0`y7SQscUy`VNCK{S}hlS44r~gY}j5KQibKE+x?mcy^&9q;5tO(_IZ1n4tz($arL@$f=|F=2DH!hpM>ztLtJ98Re-&lrQuR`9&+9VC$9ueuQvg+> z6YyPbg>8bO99EEQfXJJd{-Yj+zxcGEmp41FI@ZO{g6`GPM_=zjt099%>R zz}@rKAV^z(2u6OqN1p>a4&Xbn`gte&eb=vFfRw$wyu5C+%d8h!AOB2xQul|t3i!NK zcQQo##F?0y`tJo(Z|&_N*kNH|2?_`Rhs>mm3|(MqH#cDN`51+e#}>urIE%v^==H5< z%S%mXmzf^IcxNU1eoIvy?vM`YkPzwaPU-HJ?(XjH zt~345-e-(`ajqQR==j!}Yku$hq`_z`=V!t&f8YL3P?6qt&W=+PmIUGkhd;S8kc8EM z;6VpOWyuT=_jB8Ao%J&IC0A5NbbV*ig&c|UVn_QX!eZI@S~``!JXwZXE9$X2Gaf{LRwmEoz+ikw{)=5a`^?r z^>ztD^S(WzfdY&K4y%J+M(GZhGFj)|kyOC`(tb)tA(^=~QNP?Eks6xrqtKDk6-lH*0OqQzgO z8-7_fdff`mhNtP#Lw$_yVx(dtK8@{E-#k-=s%jOXZ6X?M%8_>e6@vR7B?F}A`UI$2-cZX6_SMLCO=}vaa z!(D~JDTn->lmK6LqZ&;_kUB0aPe*W<{AA^nT&?)fDLC{XoQneS^}Lsa)EgcRPod58 zIb7+2$p;uV+>glJ_jM2V@g!AKkR5*E;l*?-RR}n8SvbI25pu3f zvvtqb&QXJrOa92gq0e>iGgFs?*1I_fRxDQVDsc>=LR z?Q7GmJGU{Ka&jZ@tXsJq`XVr?<-r^mp9RVg16uAjev=Z0ZT5r^@f!9;G%q!mwZ6Ds zn-mdzG>7>i@cLv=x}~TX3H^Ycz;2c`jCavX;`Q+KdsMg+=%`$ZolnU`;V8aERE5D% z>>n@IbtwYG%X&O(yEw6o11OaOt`5A>F1K-Y-se-+nNORK9C4$*i$h*jhlhT;7-Tw} zwWeX9a|iHG?eW6#QpIH`=gz)EBWPa{!F74Qo&_k--OB4q`u#!Cs^{Y|I0ntle1Ry$ ztlyWA_s-k~?1z(T;L(V+&iAQ-Np6fk&1)UDM(TOqq=>n{*kL0_QXY+PFB_BECx2Bk zz2>;a)R z?*2e)=y{W9Vo6)cN_oH@k;m29eJ*|95D+LSX+;6o0Zjb>tz}Gz z$^FGI;-D3YNrB9!dDHSwQ81>wdx%1&Hj>GfVim^R^Nfw+X9*&?5VXtJ-H=_DNAbJe zYR$f;KNv>-#P$_t*AI#OFoU3?!l$f)LgE*Tf;p2~t4c}D%K3O+NZZZ1KVw*^Wo0n?&Eg61sWk&xqUAh9tr5zghy+0y*Bss z?D)+T<|p#>{JhN1H&|vo&cqV6{^YjJq|5r&S7r7TPvt3QrX?|4 z$@cYfTTasDYP_`FB;&Y}waV%ArN4Ejv`^NWS;+IwX9*~yZ4b5zsiar^IiTo*G%!Eq z&%OiQ_TGEn7i&=$Fpyv{mg_wY?gL4*Tb!;3nOfT^gRc*U659u~UDkgTpagZBvWDUF zHwJ`Vb-)kKdR${~`7E3))m;91fd&&^{^UKEbeY}#7&#al{RSX(e8qqMAr>Uoq2{U& zK&y2;yk*n&z%H!@))9JD4>&=Z-B{H;NXRfj;_!MMMcZ>l#P&0vkmJR+*HnLG3i3{X z{qG0!frzCwHPV8y=`s*f4-A3IU%B=Zt4uPZHg()F#5TQ??j89pyhqdtm&sYd zDL}=VvB04@Us=go-@cpvu3Tgv<$tk&-^;W7p63%F6^E%ztzu$W_HcXO$cy~3R8y3` zUG|}?)~el+BoOoVuntiD&+up|0*w_3?Ge`%&ZqODYcL@A6MrA$;>M7<^=gPnn2$0zz& zq+pl>i!B(^&mjdaeJTS%_NP-alw$i_4x)$bAx^|4-&qe4q@wGPLLxe?FTT#qrCno=9r9_`{T?>Ni0oYs zD3#NjEhS0+v18_-3#43e4B9_O-1gRlW!UAP-^5ABGe9Qs81!7tSM8?oIaUtrGvEw1 zowg!xE&(?#Y{5RT=mQ76)8ma*_3ZpM3L!H@J?7ZQPu^aU49sNXty{9moX|e%jfUr6 ztH&)OiMjhWUl;d{(-5imH2Yw zwESdOtc{0jLQIdR%X!svGE)>Dk4h$809wsw+_ZWXSc}%(22dflY9I4SeA3$?Hu{DA zyz#@{-Dc(Ib4}vSF|n%v4Vs=ZWHls0|I7!?F_W0O)gSO0_kVvyw^PNnHenTXj->Jm z-Bsy$wOBPdfuvdJ2sM)^bW~K>xWyt>`6=vluNQ8bC(t+!8m(*+06<)JF#WRny7ErI z60g`{e`(v_b`xlaE_dEpa~>a2(|S4N&pTK?z=~idp(}k- zDV`OrgEWJ{4|tr_WhU*1K=s{)8~f9|#AhPVlX2FWA_80DEe=S0YTY(OM9DGxPWsj< zgoz_rxIO&RRy(#x6*J!hm~Cu4M{R;MY#PEoo9$f@!`|O2Bq=f{$mop%Xak>xA<$pEsHXGzl+i zEpGdwsD2lvp8)&d{XMKgP_iK_$V5dwvH(8GggbI)SMF?aerznPY6I|&j_hi>2_&iQ zuK7-cfsx6HXGsNG1-dGMsL0fQEJN5v;(2e6sr8Mcu+SXvW{;PG-~d7%_X4=YYOA#$ z7k*BK4ez7c5BHS4#`fQajQC9JPSBIk(7Z_w2?@zq)76Ai-7;>XG0a5T9FL?B_pX17 ziK$VGMdZMLA;J5X2mDRAGXk?hM<~6Q7#hg|c(k0IIp;q9xUivg_ZO+K-R$Y~^(CN0 zCxU3~SZQhVIP16|=5K_?=B6GO!?Wj=DMRan`-WWNd_cKytkdR1WWUm(QS%)(Hig~x zEy@Pf{(YFM`4lfQE)T=gyh)mHJYp)Ol(eLztzsH*QlY-16jH+Pnm8VF1{Hxk_MxFX&Y8U z`BFGaqll57R`dDZybpXk(7&gVJTOjr!4n91riO=+BGN?YB?^neeUZWrUsvabt-~DE z<4R3do%--sz^S<`z<06UvFi-qj-Zn$cNTLY)qw zSzH!(3)lT}nDa~x84V4tu(Q;6BWsshmc7GPYCF8inP8YtxR8&}ao_pG;beh|o-Mne zK8O;5$=celgA}$#{|Smuv$qCH1^Ibn7P{?l5QR8R0E#JLC{Lwi69inKaDzCF4Angc zo*8o$+H$yY#c@Z9>4|FXf{<=YJ~s}@!l~dS_oM(zg~Uo$zxa50SJ(H=Mj7Y{GVk}K z$KvOgarY+7?j^~TusT-Z&)BI^ho}OVLW6g!z8MV_gH`f3`vKc~QyS6H| zK5=BCKV`g)`U+s0N5_zwOPSB5%Ol>SYNC?}Ok^5RKc;YeF)?4F#+!K zPIL~E)7jISS!4fz0P<8#0G}PBGtkjhmh|>;6e}I|MN)uNjpylE9r+Y?d=8)N){B8^ zI^xVqRyMu)T3JoaU|BYmuI+YSJn@M^!6#-G+I~~!b~hs0uH(pr1ejA}PEO8hMCe=4 zX{(1Fvb}HeC%{xhMeT<63@xp0{l=dE@DptC6!I`6<7zjuwMVqgW>~A_&vz=)pTw2v zBIp-R82v<4w*uZ^5bJ4v&Z`2`foYXkcTOuvlTxcSSKqJYp`?U~-bpn!4C*W+A+lQZ z-}#x&>qTr&{sZrq=kv5Go5}FQS$6muJfZR3*?N`n2+TQ;beWQgsm|gV!VQPn5*Ix$ zGUZyapOLZQTq#;6TwvD?&MdQvsIQ|Uu+KWbUQJ@V`mOhkQ%ve4664`POyM$uU|{;+ zM|ZU)UY%dtjV|7v{BVY_6{D}NeERO_gvh~lM1AWK-eogW>*;mNRgIN2=Oxc&2`lq( z|&M7iu#VDCQgNcde)$b4Okol|dZi=AbB)euGd!hV)d4Chkn; zv^QcG5_u!oL8N44ewt)AH8o8m70(dh;UUN?H?hK|~%lymj>;g+L><`yRU@3fttspq}H2o&|ul_r%wM__x1btFphit!gt{ z%nX5bhKS1r7?4rH3i<#bYYtZLuk|4#!^1VXT5Yz=v4cN0e)al5oG#XtfpWs=>ZGqD zKzt0>W%RDZKXUc>=g*9GA@-ovo37g%;a`^HxRya@s)Do9SKOa?eu)(*y%FVw6UsZyqV199$>3FjZ zDA!slV~Lm*GQy;x2Aa{4R32y$4*Pg|?JAngVN04G`mSvr)F-^3N1m&UoIYtHpws!G zd~9`3coY5D+BBJ%aBx*U0-|-%j7Nv$Lckb9CIr^I^_asGG-=Ch5h$ z-H=7;oTs5+Jf6LI`?6j>8zyoqnvSxT2FjhFjFAn$7Aj}0iNpCpT4nKpTxc*l*l94} z_*df=sM*+*i{!F@J?OWMdb3Q_aa=g_ClHM!xS9T!Lufm98a0(2E69CXYKGFa;SnUF zMlg<^AFvhhT;X?hgnVIe{jdWYS8>5t8M@8V+6klDZszibXCwUP?}`A!mk+Y$=U$bE zANsu#T+D>-*C0!g$2!W?2aRcXFjUsF5^-!S5;LWnI4EV$T%;ZN8T_VEvE6uS5kCjV zAX_tbnr^9K23|6Zt$pULj?=P4Mk1(VO_(V>8pdpPBJPeKd6=Ko^Ny?ll=HAV8TuNCnMElBu2BSlfK036*Bz#hvk4Y6F;{N>63Vv`+=G} zUvr?o!Ml$7FVv9HSAkuhL-1JK?|vd;rc9fxyt9T-RR^XMPDJLTf2+V}Hg6FG?Qhfz|v?PpXE zXQXnE;bvxm5ng8EPpHQUx4(!_ipcyM{DL&jEXExeX3rwMQ<9(bcA{Li7e8gc1DMCp z2-sqZwuA{cLH1g9`Uh243Rvr|02bi8Pe?r*|D>;zdh&Q$r!Pl&x_iib(v6GTdMO8} zHoem`y)J^k?9HhW5Y4lCja|siz>k*H?6KK{THF}`&_{qblUV*c@O_ue%66+S9_S)z zA2e&dx7|)#sK36)nZIQEMmG`}7Sh~XeEP_n%0m6WSU^4R33}=3=E?Vygsb)JFvD-w z$&8P?E1Na2)m-Ke!2J&fB*2W!DooSatm+}_LwPgj=_u)%qpS213h=_=+y^$D?-_IQX9UPpMoz0?MJ`15) zJabWo=eu<%AzVrRksLqn%UI4{Dgy4aE-cL68XkQYCZ%4DnhjEAqIw!89ts|l*c*1a z3#i`TXure)q;+A!ZBYVPNEw$pS$z>X6NiDl^3B&~20OQrat5*_!Y^Z2MmQ=c^}nMD z4HS#`)uJJDP%EqPT>3AxqN<~I{-SDSW(=#W$U0zKVdM2E3`BD&Dl3?(jEzA`NFC9Y zL)G|93YRF;#qKR7w<9C_2uAl2{G>#reZW9(@ixGJ`v>y+zU$I4GhRqhN$~G@yzO3B zxw6@wpjst3`%eEbgDzwyCV9NMqH2J2Ywqs_MejOsou6h==D=tk*XZe26AJdf})_XTjy10FOxi`++JZWj`52JF%G zrK2ikCld{WAf!+}oqKa*8-=swY zeLhDr8dStVdg0x6B7c-14wYU>YJ+$Xy~O!&^VA5>k(mY<4EuzWGbrk|tBK-7Ha*i; z(+aLoOj#DknU0K?G0}_{Ci&EbCe<<$ucv!ESS~NWFsXqT?2sY(@RqBw9k;gy;p!T+qK)xeQa@mxo-aTpy_D3gDAC0t zQ>tqB38z;5foje{*908K@#-t0>H-Lv6QrTpqqNzJ&gTjP#kNG1hjJ1ULf@|1;4_5! z(mI!yTfmaVH>&B=`WB{ot-_#JcfQ5Vc@FjmDbW@V9-f-A8qCt1A;X}gu{++JT#(a(?3BqCLr zh`jL%+l7!0mR%ajjW&fZA}t|zE-E0fMbDzDkmw-mC_T^6$gH*Zrk}|zL*Y}hM1m@L z6IesTVa<&iZx;=-laDEQ=*{;5Wp_Idn{xImsXSC~(H!NkV}&$w0!~{#D*v}Vl%u0R zhq)5<8j#?Z>eY~{eQTdmAk#j|Z=DyI?UKxn^2Adm(dlm!jsA)t+g$eny8vG^9-pZK zu>B!#_7HKq7X(+ZEPOKilHaPOrd$u;$Zt)ae?L8&Z{?@ED=h~p=3geVJzjT9$AO#& zbklWUp8>CqP$`#_hF##Df^1p~lyb36!J97t`9_zkuJ$j!b<{E`KPA}?1s-g;m3EYs zGY*o=O-6~CEuSw2Kc0yO0tVi+bf-*_{0C&iuqoQEYuI|%i7Fo<3 zejobI>*d}&2J+{3)}#n|T3s{%4uM4oMJ0P{HG$K5Wa&qlOuAl?=;=yJCVSxi^dX9v znE0nGZJ4`bD}OBOkXZ#<@6Jz(!!cKtSDF z$SvZBE?&pFZRY(mQ>B5gkPf=QnjHcWl zyuPRsi#!RO*osUkaC0oz0eUD10Wt}+Pp~B93RJLPdTp=Z4Oy@a{Rg@RBzYsh|CLrVE;{!Mq4NcM{@+ePrr?QYT za{T^gcrQE*3~0sF9o*~*{X!QjF|7jGJaJat__0F4k*Ew|$xmqB3<|cXpav;9kaElU zP8;iDSI6sQwvI{o7f1v(OU7`Q@1(+5TX4~9B(dKoc(~(7tM+H;WZ@O7^e}T`4g8wx zTfO?_+ieKrblm%24Xi6MR)LjS-)#1w|9-3L{C}lL9JI0M-Wic4Istey_JHZsnXGKTd-jOJP5_ktY_p1ZKUfz41i- zkYVQFti0AIR;fU;eRM&0FYruHPwrf}Sl^QgkvDfhPS&!Vgts1JjO8UA-*#^Nn(8xF z1Y}P=xN0S3WE`BG%QVZa2Bip~uvOs4c%FWw!j$f~^K)$Dy8KN?IBQXzj(qGR4f!&))@u5Hb=H zXrKmOe)fY>;R~>f3eMyX0tTxX1(Oi#=TU)$L|yO2Hc1Bd-nSn=?#IWkkMgy7k93*E z10x@JL3C6{r4hrR2)SLQe~RM#);xHp(5YJ4iyk-f9VbI$QlPZJ3N9L7fB7dJ+ZixW zLScJs`pC*Up-$pef+*9-0ca#-;Qb~ zM<>ap0V?g3Z8Kx@Ksb}x_R&&pzp8QP4{>-n6C>lyzJr3K>H>y~UAjOxh^9iFBTd4G zl$U#)zj)OXa_g{P+Zj#<)U0_;W1?ZDqJ{>Nb40f#3qk1n=)!_}gi0R?^??1weDtM! z)2Q-k^On;~9^)#xyp6^_!gZ%UiBUX3y_5o2_l!xQQ-gssk^=eMwgQbrvso=;tAWV; zsey>|GQWS9;FN-t4OM>nPd8(~3XF+%<-n>4I_9<723#i$;kV`^qHlgyCFwHigo(Ci z%yVc?5F`0_dLq1_gK4uX2l-Q^qdA;S4$oV{lj(i;BmUckHJJ+=C3e4s?yG~dSOxwT zS9oZsrA&i-!1a}CHsPR2%E@WBd3tDRrO=yH1pdBwqWJG+cO*DDg@S91WQ~aC{QT?I zrDTmM)3vecPp9apKx-2HF~W0s;j*ENJmRh9suv~$4)PKOEG#T7Ep4BsHQ@gEcGust zCJ)n?qjfP;N_IkLjv9p5n9skRV+tw?rg&HoE)PGkh}X&_adPS7Kfj2HkL(gD>i(lJ zR!ecREn0pth5n|7VRio2N5ft*5SBZ#4F#X|g)IWH9Qr}f6;l`-LUNAps%*8KRe zXmFGX;-$)EJj;!QReB1838)MlGVwWGi*iSc){@!v-vIpw~8mblf&s6<7Wg;;p@qHx;HSJhszuW18y$ zZHqSg?#JYz8*0aHB9&uzcw}c=Mf$D?m<~xD7=-@C=skVy=R|~Y&(o2>?fq7IEehUhI?EiciK?q1FT1`8H0jNbi z+As3|)Bw+qw}L+outN3?07t*l+~9#gkN=9q2LurE0{e5cNg;wzq#AvrY?TI|**rnr zW?h}Ly1Kftv9X*S8na|ZuXznY5X;dYoi>57582~6Z_w8x+190Q=+7t-W5VCiR#=d! zAyVg!pzUL6?^hSTFZgI4s)AgsS)#OFQ*nWPl7@MRbX1fbqyK%9OD`^{z!%3z`fbj< zrMTj@*uCp|!C@o88XwDioVgjiUv08=fQ{JcT zt!Lha8fy2i>!uH-YYWAiin0S5-tv_w(z8-zG+Z-$O=!{mFj~O&K#p3jBnpk;pTw*J zr{oewP18#PH>R!Io%M&1n*NU;bS9NVMlY0DWFKOjcnNm)b+D_kL=Hj}r5es0Q+1?^ zHf?PcURYnvoXE)6#;|kvZ3@wVkT$%3Fb^Q2{yh#&pi0qltB0GXr$D*Rnp~fz8kpy) zh{`Y&b!3vH!}&xOi&JB(C-ga*e$>C-91>ewTW_7b+un_oi!SR_B7#5dHfQH_vO!=R z8E?r?HV!kg9nWWx9M5T={vK@CP`C96Q#u{AQiW+vJtozdQ#}Q8r0VPI^(>+1;*;Ea z1=~pyc~-5UzO@>1`&4KM_aQBC7*b7ET`u#xUtHMn^YLF^Tzn+3k_eKIOxrBRNv#V| z4ifgV4TZywk4XzOaJNzL)__$`5kKcWe=NH~p41?Kc%}@%aO6x<*QHRxqD#Xb@ zFHHZ|By0z7Zcn;?>-}c*JKByV>My{GQ^A}2vBDsn?;>d2RHi;QL!}5PE-A<5l*?>tu))V zhz-ub-rrM-b^q;2k}2$T5?lDs*zgjly0c#mYE*WvKG+#BXff}*Jn9A$# z|C5l%B?HXnyv~-^6UAmWHhcY5@ggD=OgfDd%(dgO^zox9+>5XMWRK`CIE(fD{4Jp?katUDSx(Sn9tEU_s65^x&>iFH|@qv@6hlAi zsE&O3=wknT4nJv_=LUBnXjTT6z4mC3Q-qD=-(jloXUgl==y_Nd?>#e@P>I6z$;xAs z>}t*T@y6mDh8#Jfq&#F~S@X#Ob-j}Gga`suh{q(hGCxOItz1JSLs}7x{8fSxU^+yfR5epD!=R zHZGD+u4zkH=HD~Z{-0;gLf7-mp-Jo<$@u!_}z9XpnKY*9=!a>Tpf*qkIwXej(E{D$mU+CwbIm3 zLP`cn5;RgAIpEuygXsY;-44^Uiwjmf6pfM@0LkrDv=%ckQ?|<!(Mg~Ft)Ucu( zE+@qKPp2JnxpWA=QiZy`VTD4_6#ixZ2iu0dcay6y&$h=G zau!x@(2mcb=52kHO8XCqI82wYT!eYH81O_C|L2K~BLZTJFKY!O3M= zgWAe{=LsliDEH=Nwi82mSlA>ywD<*Bgo=K0JIjp8W-JW6xsUJ`gmAKJ>u7vx{crA4 zWhe$&Uwnv92w<%-{!SRZWdKv?zYQ_soZ5T)X9!ePTfCAirkC?K!zV{?JuPz$4P`^h z7w;N`Uy_pRdV0)^UKu8ZP-(l7k-L7aJYdvSgsA+aNcl!X#%>wrndCc+40+FnrFQOw zegyC6~e=q!-4ePNna@?Y}B; zxxv*dn79NyHSW%W9aBlzZSP5=r~BFSsz4(f2ws4G{xEyl^I`upx;0bu{87>6M}klr z)(B?dwX9o9+M7!8Tn2jjao{1X$y1+v4~*#6Qw550avY=Q6yjk|_q)q%LpM`~Kl?zWdTe3Qz|DFv`mkM8n zZPS=449{wO6Y(HC`l^9se!hZ%rBd$->`VpP74&q?hyi-9MCYu9F^{PsZrK z$$xpBzx#d}c{H!H?CNa!oS6zmviIwI&N~Qv<%=kH z`DH>qhBe|vf!_L{Q}#Och8)JzfH==vB?`dM~d(UF{r8o+d}eA5F8985(S&iC^weh8Sask+j{EWu_z~?D zlk4lS&@|EvEidPdIIX|Y(D`Av#jWvHJS#~n-em^;*&3-t)+_SLM6{|ln|l47kyp&r z_RdagmCgE}So%-B9NW)%=GWY5lZU9Lsqs)gP``4`Ds~b0ybhf@fmo%xw-?$U4H+3T zCFF0$li6gRz-woHFUct`7SYQ9ag~b(C*hhGp4CC%$7hg(z)DK@q1fpI{X)R4ywe~QZ0}k6^6yvb0{ugMHUdbWDZvCXcB65B0Y8A$^??lpq zYy@3SqG0%fS$>P{EjJeuTb}%^DSPekXxp9sdZ!p*UZEjN*9(9O0`mG>h23_i^Gd_$!9WCA7u8xA zt$&ChMA$ZMlKuw<$tb?jcY_Qmq+@hRA2(sdwzja#p-N^Njd0^xL$Ik4yu8w*^!;+Q zcNmNfOjCtIkwynUbpM!HmL0Y)mMUD6n8=tlfGM14%+EEVi}}+|N>sjd#rq)SbvwZd zP$jtfsujzrf@xpxFO+Cc^M)0=QC~R_tJPFO0aQ}{-yXr$oy5Q2PPa<%)w+Wd5vYen z++!!xL-r;Y(49(F(;F^7ozzt&$-Qp$)UdbsufsDnmapx*hITEN=Ixt!IU-+tfmz|` zvG3n|5Ejr!=}WZuPDj)HEoAm_Q7OO~DWvH1ex~QA8FZ&}>JYzsZhwi#vE^;{tk-$_6W{GjhD ze-YXVr?%sUv>@JodAUS|YUysYZ9Y4h**fmEN0LeuvUXK!E6;C&6@93%5JTpq`an#y zoK?C96N_6;y_0^tI^yM?KK8rcsI08%dSR?Dwb$N@KiSK}@pBv|&UdMwgS2e6sqfsv z_Xww|{LxZp)jhO&YwuFzopSSbl3JDus2A?q1Q-T5mR%>;i{-tf1{uoPLmj-bN>=C6 z$ok$sC5x&HrVh14DC1ycd|w^25Ls0alz%9^Wz5@7 zH>JkG(jx*QB&IcWdZ<+Z=lAw+CJ@M!+wWzg{XP1)r?2?~eSkntKyHHJ;rY0^qb! zAnMVpDlfNL&k_qnBbqR+etx(i;;=LY?j%Js@S--cap2nTTmqJG%?uKmc{@9n3Nyt82x56IDlNRpYlL*ufE;cKUsD zIC~gM2(F~vD;bN&KuSQX<4*i|D+rp zIfYjX+UT~4m$2?hAHcxlh<`p^ZR(v|M(uT}31*}lQk@763JT)U)9VdO-@8{^&FzK3O;Hj?ImA=5)b-?!UbVW}$0Vt~oKnJ#lKNsdoQ!57>Pg}(CR})J+^Dw^ zY!QJ!5I7<3WrH7-I3kMiZs+}X#y0n@WscG*w(NG4BitZ*{WcOy`TMJ0cADDH_RI4> zqxN-{hUhBgj^1fl>cTgB5RbgLq>wPL-&u6~;iP?zYiIJbyI$)S(QOE?JNFxOw>J>+ zEOcCG4{Cl}5oJTl6G@67g!Rt<9gE~KN>h)AJN5xeen8BwV6)woIfk;pkPfWBkdLM@ zrv=gRez8C)=ik{!HXQe81WGT5@HP6v2UK`gru-PIdIQ zmvwivPe@v?O>f>ES~nH-3m-$jtqc)r8+{*h;*u?92ocnXf>w;1qp0u+^*A=g!1p&% z?N*lmdS(ROS14`xLGg-;QXPNMr;AIbasa2z^V@TnDv-oc%cmWI{6N(C{~z;2GXLYX zuWu)yh2mmjGAzep0?|o+S<%d_T5{ZyFf)JnUC~9qO z{3l@v^|&m#2kk@Pm>1bi02h(x_4TZ6iDtN~xiVnLQX1?thj6uBT0t7iSp*ppEdO4= zPaWo4&|s995K-@TD_;};Z&x7TkTy&qk45QHq%2fgNAbg;T%u43&We!|p-DggjUQs1 zNYQ}i!xRoPY;f`a=>obn)FW7YHu-wHz66mXwD|n;34A9^^HsUYX*jmJGJ`p!B8m8T zB29(HB@BAtXn%bLlqL04u&7jxTe7MRiN{qmy1CR_)bKYtUXGdb)>?;0BDJi5XnB8* zk`0>_t2adJr>CNw{Vs9j2x)$Gcx2`tR2c_Cs5S+nHO8ODeT@1d&Pf9PX#?xw1DufD zos0YUXI|ne&YJ#8_Zvxw2A$1(xn`s~jJ6L+m%+le5RKRJ-Y%`v5QqP5Tq;O7nAoth z^O^U{s=L%wKBTI9qvqM6D1}eqi`{#4C4!%(&Mosw!s}%vO|U5?U#oj(=7-5V&a#l( zeSBOYIg7NZ))Fr{mc$Q#CHAl(<`%q9Fb%hYti|8Rf3VWC;F>Yqb{pYG#d6R(ji{=V z&S)?6=*mZX6I$nk*?Q~28nL^iPaiSODBC>SLT*lBg8Iu>RA42Rqt>L4-yb^v&RA~J zIPzrot#qc!u|W$*>jKt3iFCQiD$+e5`4Lf z9ydipLQyF)o14z?l+o9R8mus{F^x4~#piW0cV2~2+Zje+m-V6Hg3OK$292EvQ9L-F zWR6gjfruMR-&|g4C2U!3Ed3N_$lIkw9r8ns744h%)O<)0Vf$_H!VQd(RX8it%FwN9 z^fj=Jt3_xD%~iDMnl!Ng+-yEB4USDQm zBt!9y&!8hFNPm*Ows;o}25-)?z73jw?~3oT4!-K> zi}w+%RQ1zbjlPtqIYc-rGaJ?PzS{ ze)A+S;5ZMbg)AlpxFMOS^$O{>m0vtgldFXtXq;pfO8$I2pbd3j!umJ`f3Z~ME@QWV z(%hnxk}mt*j|T<T0YNk!9@Yw!OvxZcR@it&F*34TMozL+ zyEF7~mQ)_Jmw+^|2yKZ9R(<-ryCAu7!Iv#eUrJu*0~oQ6)3j@Vgj$OCH`KFg3^-a^ z)MIGXooYX@OUgB1gWE%0>187cdoOTs!bnNWxcYt9U`GZnxWp2RReW>!_)Y2kY#9vn zu%h7Evb1J=y;a{V4{!?0z90L?J}`*_B%4^TeSXweEQIxGY1|rp^Q|^Ydq{*1Y zZ=stEr6<5In#5B2-c>yMC&ReFkLMSeyLl2NOI_ZN7irv-7kDOq4Iy47CJU%{V zJK&{?z(z0~(s4QLjWu(w^o`;!5?stpfYKGm{wGRfVLg3ABh(6dO?M&KJ17kNgb6b3C-Lvgtbr^lt$f8RZmIdGOg7A;0F{0FK-d} zUoAP1H)dgC7r(&PDd^Cj*DpUViH+|d@GfPr zko@CC1j0?siypt`(zZE`)J6Ws3hO=O77@8+x|d`3j@w?lIBWTmcLn28fm>LbR+dja zM2ffXwo0lCtuRiuzD|3pHXE>nN-?TDP_6 z&J-tt#zG#Ov$coocWDIISKY|FMTUbz#ZScRK*)R@v(%-q_esdZ4{9)*CHnT_A~ro; zP;Voryu6%AKxYC(Kd()MSD)%G@`9Ek!1w4hyPWThXRlf%Cj|wsqC6y5=I*G7Nte8%HR!zi!3XN%BxT3o~Qi+%^jpo9xW3l$m=h=P= z&$Q_JYnHes6zSNk_IPiCvHnDk2bbYbUu-HBPESykS^V5Q8lgIlfcBGFiwyg1fMOdX z+~M1v5g*A44+u=(|3qASV|^B@{=UcO4IEG?PrI{K8&g_tjs=HfeYiRiqdfnjp*HPO=jIh3lSN4%YXVZd*;S(HB#bgBAah zb=UP<(=X{2gJnwt%v0z%=-hU>C>Q8X+wV?oinEBp zK|BcBB-%%UuN121lTF!7*4Z6=SMTnjLUP1e16!4=5fXF8Z{O~b&d<#ykst2tn3|YS zx^w|eFumv}Z9ww5?vmJ#@4zWn&qtQKwz!^@oD4q>eRCh>!G94i$TP>gRAaCXmIQ%% zUtjLUDXJD@8i9ganboNrVS7=eF){D+CxG9XZE;xd2>=||CN&0ZdL&!qfPfeV1MH)~ z*a|}%gaFK@fZA4~*tOQPeHHQ59!CQzW#K&ns$*qT(ILc` zvQ49w5&nIflaqEW2wDJ5@9QvtI>f3hk=`6Brd?~%{1(Gs)*Qpx_#;ipYB}tmc?$ia z5FIK#cpaOT{^!Zpgz5e2FOhxD?++Ua?8<+oV-Wv%J@2G@#7WBlwf)jXXHCk=O8IxE zL;mBX(*54)t1Nb+;~GJL9ifY4bsk7j|3-0%RUQ+9DNT=B^`0v|CvchQ!I~QHwb7#lg?rvA{O1@hk z(5t7Inc6PJsNop8N7L*1;Q7R)!zA%e+uX7&$Q6;{rGn5KZpTg4o!Si$%#cpgdbw?p z;eX*DJ)h6}f)oLD$0+-O{ekhS9jEToZ*sC~jhYu2HpPSGBbMO9z*@*tDRmLdsCc1>`T>f}U&4MZ@(VJrWcyaE&XPu3c z6C1+9QW3@14>jM~XrDLE#71)73%ly+2_)t+dIGFNPEKoC9DyzRSDm21z&s9^?w2RP zGk11#8~D3{XfnI3EVe1As7PksJ7IrstvTdwGFw24rQR{SHFf#N?H|z#F=3Z)Kh0Mg zEUYy(Egc*j)RG?-P5VNTOBhP%71#}%N4$VWsiGgx=bgfU%#+ItL|wwV@3#Mlsv?l(XS1ZsLBF@qtqdxsJJ< z4Ev=OC(!8emh5g1qX+&4bad%P*#g>wd5~oavB@h|sIqeBy!Fhp%q)m2O&4=^e+s_w zO3iRFz(<4PhsNyFTdkXw5qito_zstjM{(;k8AHORMV|_(VRgoz&Uzw2Z-rP~*riOx zlu}s39m~0d-2O614Gp0FU&wH`qQt9v^%{`k5E#BvDI)ECcQ9;EsTqr0xu$_nSobO* zc)VD{{})#B3mSYt^VJ|_aQTzHpP@V#Npi+KLrbu|IY=S9QrN(^$R-qmA*WyX5qK+ zQMq*|se#Ja7j{F=-0hBkAyXt%2)j@~BsY9k`UeQ7Y3O^Ia$f~0CI-hDRQ0HWWfhz< zH-8_$8fCQ~FghR{2Rt78Inq1UH~rJ>=Brp?WAU>LcN$p9^Q3-Vf$0Pn4&bDgst4Wc z^U{YArkW44|G*?5PMxUFKPM9N1PLkZ+(gK6JP2I1!*pgyiN%aAYU0rbAjVydjRPaK zA2T%W=6|%cA5wlNTN@XU_l$yY3{roJR)}s*19oQ8_|l(BM8G|jxCHLTu*HpVj?lu~ z9#6L=nIVhj>1T#X0+qzOvks0vaTRGxdfc7CVtU;=ow|356VqHkp;(1+0}#Y@d+|D$ zVNx4H#W*=))L@RBqyN7+WM%X+gKj_mN+30F;$&JoKA+ol<#M-v_5abqb}eB>I(6_a zA*qbYy6q=G%;lfHgRrap5OH?E?sUIx@@LEcVhUhufj-qgRLsi=lhF6w$}TRG$iUlv zk1(RX-g?HIRnN1B1(G+8Vo_`kKc6YU;^vA-Ek{>2Pbb^aw{bi>)=Vy-?_}QodJ~+z zAi(x zh$@?6U|qOTMo43tlkU1~*WJ~nnYPx?jigd~_)C?*%9)YlQUK9C4j*P{>kyd1J)`N8 zV;Vlj*1A76g<7Bcy@pkXM)^k#WP7%2Kjx`r2~Knby(CF!Tt5VkO`-5(cgG)M>TJ+> zQpkUfEU5%IF2C13p`!3ksNq3Dfw^RJG36l=UX%~D_HaZ7I1g=9!9E&)N$?{MgH zr0k}=;DaA?;n$W{y&nH9TgRZ2t(q^6CGfJ|b%+w{2RXZHmsQ4QJ8pmER)a%vXrz(t zusIx4p;i-|jbTTc$zDy4hwV;%t+_CHDf=^ z(RB&%*(ttWHjDB8LfKu}L-e%W(y#x~=$qh<{=D-GC@c|MV_r z?=*&f2`n?k{Zlgh3AaQ`yv)BgHsI6c`97$xYS(ff!C$B}Gou`Gh^Kh^#`a4w>E`p^ z(}cMuri0CJ@9_FV1__DB->EeWOJoV_yQ|w<3LeW#z?A`T!j19s;?K9t4*(WrZDj?d zhfslWT6gyo#Y0Wf-2mt|8RZX=N8 z3I>6!1)YlNmMR^}rL#Mr-rII8t$1({Xj5cPFVvJvpa}UA0QNmD0j^@kT`ur*0|Ntq zlQ_rd8z7h9_CBZQrs{m=UZ^3qwL(f5-kB|1jt#L_NKvgoEkUr52jaOArd{j^CwaBM z$)d;X37_Y|)~QVqA~Et_#J8Q-a&mJ&SUzxvOZIWc)#aN2GiL?0f`Wpp;l1bQ=Vm7- z&}HE2v%AOh(T)g>D%ObC?PapEkx13z$vWUb?eXkfw&)g4K=JYh$dh2W*Q6QM)KPk% zn_^=b<2|wYx-+0og4E~(e(s|$9)Ep(g_M1Kd`RN_1O++9 z8>v->%w4_jgAaveSfpR)2>XJ=(eZ5*u(6}dM<7)y43LHPu7TL9TZI6fKnmjXft4=5 z-9mT%BPFEiKJxfs*MvhtB(;}WkidTo*8gGcOjG_`CzI4kG=L;j$O41RLt2pIu!8H> z3LjSxeZ;S)4UsRi90(4yKTxLr+As9CBY~3^zJAb>3NS=Uqd2egA|XIeA^neY8nE z{ELRq)N}J^ed-!Tp*#a@Y~!qdW6=-$)+)K$6$=L69i}Emben$#iMBqwFmVuO5z5nu5#HR~ z(9)fjmX%EA-1#{a$@&#VGofyIP5OijhPh#wTZb);I+&nTw52{_6Hc_cchE(Q3RBcLyaTQT)Ynqm9_yoHg#o^~+gS3xHMm z&m01O+-3-c=xwr9@o{miZ1e6QemdG_{rk8$%%C7wY*vLNpq|U=+=(9i@8RDJx{Rzo zs~a4LE9I~4F)h?kl4)(+l{SHz&b!~PS5L^M?T6DjclY;>8_lkIdYS4KHd}?HGY%od zVjn(eD(d4RQBgS$nJ<-V0ZDBSUYmZgvkPqs`%8{{G3c>;`YmGC!H7P2-CpzV9S=DvlVzEByY^Z7EJ*i&3gAe z-aj9SEbTvm)q>nELt{zk;j5OlCf@_U%XK1ZMHtBzeLO9FpFMdvori&mzE?d_$)(Mf zuINMECGIaUeUpS0NHN^U0G%YlQBl#oSS^Z_tpXabXY3Xyv-v5xP2qA@OnCqv6G*uO z#5C6QvCV-^8*Vr~bKkxKVlE2z!GcRSIn(?pBe(6}%Y^tg6H|1EIY&3`N~P@hXC}e7 z2>anwOleKvl2G$FO!-o&&OeTOYd~J7GA7}tX1iH*8%F>M3cit20RE~F72-+Wr z90n8IP~iA~bRw$KV~?Y+n$93RuRJy~$3ybpteND@Ay(}=tc=`A`MeU6m>9F#=RImL zc9z7o9#mJ}>;&PPfbwNRWv<mb`(ob2w}NB5BXJtOxN{KcUja%>Y+wqj6ts+S-PG%U$4C&QY&yH4Yq)SZz&N1Gq94O!qE|M?Uk*QfrS2u^mKQH( z9z#4D-9KDB+NzGkID$%`xx;E>7ydeO9o>^Iumt4`A&Fw8QC^09YmFlz*9S@BsYq_X zB-E}&z|=veP`*)lT+i;%;3y$Z)ieTIgPGhn-Tel_~y1K zSWFE)uiGB4>{-^6ft6+_k4z;+Qx!c1Mq+|PWxTWF0ct{iC&wO3fY^Z`gA__Pgpiip;^X7yiC3Kca1H!UnSTBYLh?#@8*4*z`hf`=e~ zYa+T~wV^AKf8qygtB)Qz)n_dh+%&sW@QRXA7V@|V$RR=q@Wx3X$*9w}v;+u}!MI_O z^zzIf#axAgsd1v)zJBF4yV8$tT7882m)4!I_EhF=wxALDexx}6QM0<0d46(I=Xdse z=Y1B&VzR?eXmjy4_{6yyoi(*PbTr_Nc39%{W8l5dgH*7;Rdt)B;vCh}vcxlNk?Y4n zDgO7*v%~BPhU2ri@g(k_{9j+j+2`f)uyq{aaqtPkR-0`Jn1av!m4bPjA@k>6{-rFj zAA_+TAAgJ09Tsf@T88@(X07Q81e3iRXL zy4&j~%E@nFPLPCFD!L!&Q66vZ>Glp99*Cv>rv6JW`Bk)U2G!Iljj-ha8d;6oY{tUoz94FSV*T&Kst{k;D%{kdNd7)J%VS>q3$$NA zCQU<_R{d;Vv&d|);t4{haZCEvpw}NFb1&%-K4R|Fo^)A!B#JD($FdZhgx<}^s<(*b zq9$TdLH*1#jQ3(QhUAVlFDn~WoVd(Q4?kpUE`;oZT%4LV1ua`An-ju|2+3Gp>It}5 z_WY#9%iM4{-8EBNY$B{!KG_Ym&go`JR{iaoEit5&5T6d`r~0QMOj^0y!qxtCW1_-UU1q!~w2eCktZF^HB+pE|TF28*)m5>3fe7FKE?NNmZ>^7C z7c&DgEY_Ib!;yn|(u*Sl9_6f@>0{W#5F&g+;q*JYA^Wuft0+th#Cq|9LptHU zWY?4)5|@_UueNTVdl3<5B)k)D>mn3)Kd1QO_w%=v*}|;nnv#T-j`R-Q>^Yo#Fe&cqdd`H>< zHy@Zcp=6r`=@G?jMEJX-fh@L?bJI|Ik-s2FzeA}#?GsEZWH?Q|8Q*I2EMogrTOFjVTN+j;#tXp(gp8r&|irLY9{>yT)w0w!Ms>=S(?|DV((92r% z^|!!Emtk#MjJ}(hVAUq)bY22Nf?skJtt`JjYtmwT{Z4jpuNrelE;HuWs|GhOOr2kt zs_aTy_XZRdEfQRKfy15Ij%8$E7Uij2;k%l;&BNJ25ZYUcI2q(F5?4<9Q%czerd>7|bzV?+^^?W!)*EMr9voO=kn;wVth%ZaO0B@G}je zj1vlemW2Jgo~V(#lJVa~fR!TXF?PScT!=qi4lH-=rQ4sL!mi{4d% zuW-a6!e|AprwCD;b?gItBDu;9sw89-v`BKUBy!(yO;`9P=o5^F(e`JO%zBd#AA>}j zT=piN+fQpdiSzsq1XZ5w+^gJQ5fkIU#;xrwh80AHI-;(_SHpoFLeMu7p?^5A_}~F*asDx;V#&kD9cIvvz|F34->GU9y_#AXZ!?{X4&2mPFkInTm&<$Sr3w{lR2(NeJKHk^3MQ zSTb7%BroW?Vt#qc$K2NHrorUt%R%Ea`KkS|N*7E;CHLR6bCCBPe_i}OQu*RNhCwzt zyH&X4dp0Jo#qqij{MW0|?R&r6F;L5uTr5KcIS7pzyfW*@C`+jiCi}Fe(uYCap##O zE)j|LZJRZeV3T)<3ZwNDeT%h3Gy#grR@GUioKEfg>rAve;`BRwk(N7hZkH0HMQXJ5HpHH_hM^%CjHulg{8RF42T5?!q$@(;jnrP3w*6#y^ zSU{Q$M=e?L+~|ee-;g?M9kWF0aVL^6IAb*!J}tuyN5NLQzoD3l@rgZ!`=1|sN>LW6 zSMUjllZAKh4<{xnPlc&j2L!k}d_jeJ+S#FB`UN()$7O_6ao*D@)nYq5$h zZS1xe>=qm!XNH{g>Rddg9ComSv5dGf#^EACi7ql7ZkHXk)>(`c7|WPnbz8COBAnnb z5%>Bbj;or=sWw^I-|CJVA^e!AOj>~!n5v}14Xn8gVDw#Cpy_o(q4c7^Y5x4mAQpbk8 zEz0LzK5sj|GP77zR z3L4cdnvU)XiTE7N{8{>3x7}@4-Z!$`R~| z*f@0#9R2YXZ|AJU93sUOlVglPzHB>&?Nb`S(Zi_*|E!#V)fd$8-`C|rE2aKWN5Bnk zD*e=?!sMiU9?iMqZ^W|EeWfy&ypWDewrczD?Z1E4;pH_*C&_dDom}nQvhX;fC8g*- zem~9<+52QjkKk7)HyKW=8JmfUfAy0qD8SZH6k&@-q@*JxJmGzBff-4wfpDI~Uh;b0*5K zqz9!HB09a9s79(i=q|*v-2eDX!cg2k=&e*RjDLiOv!NX6H{I7A~Z z)jf9$Ba-utyPn`YyxLLXN+LUk0N8=u?0T3SbB^uUrcGbj5u+m85NPGqze&Qzev zD|eH6&N-HQ_N@R~D>IK}3)=)zeA_!FkEBVmjl9Q{=grgCnD!ZP)Os1cT!G1g+c1V}xl^)&))xHMx1?0(z06WJXQt?m9 z>4d^!Rd!754EzL5erBW`Den{T+dZJgM}tu#NaG-yK+RB=R{Ll4k+KAXN1**mU`Iy{#wtDopoFvs##@f?2ImPeU(%Gcw@Im{FihWId z5F~{Scf%&SJ@ruoBV*saQ6?rvMr$03Pi;2M(!5SMnw4TOH4?7BF6~56DWdajX^GtU=Ma7y~v^(a0>uc)$|HWsQtaU75Li!JCw0)gGLn`N&MVl`k6X4g-RK;IDR?L0_(YG~s|pK}K1+R?;-|{{U$$U-SR~ literal 0 HcmV?d00001 diff --git a/store/assets/small-promo-440x280.png b/store/assets/small-promo-440x280.png new file mode 100644 index 0000000000000000000000000000000000000000..15e7bfa74c3cee1074d7fc278150bf4adf9f4d71 GIT binary patch literal 61880 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 literal 0 HcmV?d00001 diff --git a/store/capture/ax.sh b/store/capture/ax.sh new file mode 100755 index 0000000..cc24054 --- /dev/null +++ b/store/capture/ax.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash +# Background accessibility actions on this capture's own Chrome window only (never foreground delivery). +# ax.sh list print tab-strip and toolbar elements +# ax.sh press "" press the first element whose line contains the substring +# ax.sh close "" press the Close button of the tab whose line contains the substring +set -euo pipefail +source "$(dirname "$0")/env.sh" +P=$(cat "$BC_STATE/chrome.pid"); W=$(cat "$BC_STATE/window.id") +S=$("$CUA" get_window_state "{\"pid\":$P,\"window_id\":$W,\"include_screenshot\":false,\"max_elements\":5000}") +printf '%s' "$S" > "$BC_STATE/ax-state.json" +python3 - "$BC_STATE/ax-state.json" "$@" <<'EOF' > "$BC_STATE/ax-target" +import json, re, sys +state = json.load(open(sys.argv[1])); mode = sys.argv[2]; lines = state["tree_markdown"].splitlines() +lines = lines[:next((i for i, l in enumerate(lines) if l.startswith("- [") and "AXMenuBar" in l), len(lines))] +if mode == "list": + print("\n".join(l for l in lines if re.search(r"AX(RadioButton|TabGroup|PopUpButton|Button)", l)), file=sys.stderr); sys.exit(0) +needle = sys.argv[3] +for i, l in enumerate(lines): + if needle in l and re.search(r"\[\d+\]", l): + target = l if mode == "press" else lines[i + 1] + if mode == "close" and "(Close)" not in target: continue + print(state["snapshot_id"], re.search(r"\[(\d+)\]", target).group(1)); break +else: + sys.exit(f"no element for {needle}") +EOF +[ "$1" = "list" ] && exit 0 +read -r SID IDX < "$BC_STATE/ax-target" +"$CUA" click "{\"pid\":$P,\"window_id\":$W,\"element_index\":$IDX,\"snapshot_id\":\"$SID\"}" | grep -E '"(effect|mode)"' diff --git a/store/capture/cleanup.sh b/store/capture/cleanup.sh new file mode 100755 index 0000000..04bad25 --- /dev/null +++ b/store/capture/cleanup.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +# Stop only the processes this capture started (by recorded pid) and remove the temporary profile and state. +set -uo pipefail +source "$(dirname "$0")/env.sh" +stop() { local file=$1 pid; [ -f "$file" ] || return 0; pid=$(cat "$file"); kill "$pid" 2>/dev/null && for _ in $(seq 1 50); do kill -0 "$pid" 2>/dev/null || break; sleep 0.2; done; kill -0 "$pid" 2>/dev/null && kill -9 "$pid"; rm -f "$file"; } +stop "$BC_STATE/drive.pid" +if [ -f "$BC_STATE/chrome.pid" ]; then + pid=$(cat "$BC_STATE/chrome.pid") + case "$(ps -ww -p "$pid" -o command= 2>/dev/null)" in *"--user-data-dir=$BC_PROFILE"*) stop "$BC_STATE/chrome.pid" ;; *) rm -f "$BC_STATE/chrome.pid" ;; esac +fi +stop "$BC_STATE/demo.pid" +[ "${1:-}" = "--keep-state" ] || rm -rf "$BC_PROFILE" "$BC_HOSTDIR" "$BC_STATE" +echo "cleanup done" diff --git a/store/capture/cmd.sh b/store/capture/cmd.sh new file mode 100755 index 0000000..50b0c7c --- /dev/null +++ b/store/capture/cmd.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +# Queue one JSON command for drive.mjs and print its result. Usage: cmd.sh '{"op":"wait","tab":"demo","text":"Book"}' +set -euo pipefail +source "$(dirname "$0")/env.sh" +mkdir -p "$BC_STATE/cmd" "$BC_STATE/out" +name=$(python3 -c 'import time; print(time.time_ns())').json +printf '%s' "$1" > "$BC_STATE/cmd/.$name" +mv "$BC_STATE/cmd/.$name" "$BC_STATE/cmd/$name" +for _ in $(seq 1 400); do + if [ -f "$BC_STATE/out/$name" ]; then cat "$BC_STATE/out/$name"; echo; grep -q '"ok": true' "$BC_STATE/out/$name"; exit $?; fi + sleep 0.1 +done +echo "no result for $name" >&2; exit 1 diff --git a/store/capture/demo/index.html b/store/capture/demo/index.html new file mode 100644 index 0000000..899f39c --- /dev/null +++ b/store/capture/demo/index.html @@ -0,0 +1,85 @@ + + + + +Book a demo · Northwind Analytics + + + + +
Northwind Analytics
+
+
+ Demo request +

See your team's dashboards in 20 minutes

+

Pick a time with a product specialist. We'll walk through reporting, alerts and sharing with sample data.

+
    +
  • Live walkthrough with sample data
  • +
  • Questions answered by a specialist
  • +
  • No commitment and no credit card
  • +
+
+
+
+

Book a demo

+ + + + + + + +
+ + + +
+ + + +
+
+

✓ Demo requested

+

Thanks! A specialist will follow up with a few time slots.

+
Name
Company
Role
Team size
Topics
Reference
DEMO-1042
+
+
+
+ + + diff --git a/store/capture/drive.mjs b/store/capture/drive.mjs new file mode 100755 index 0000000..c29a4da --- /dev/null +++ b/store/capture/drive.mjs @@ -0,0 +1,122 @@ +#!/usr/bin/env node +// Drives the extension through the private native host socket (protocol v2), the same way a local agent does. +// It stays connected so the session tab group survives while screenshots are taken. Commands arrive as +// JSON files in $BC_STATE/cmd/NNN.json and results are written to $BC_STATE/out/NNN.json: +// {"op":"open","tab":"demo","url":"http://127.0.0.1:8765/"} createTab + attach + bindPage + navigatePage +// {"op":"navigate","tab":"wiki","url":"..."} navigatePage within the bound origin +// {"op":"actions","tab":"demo"} observe and list action labels +// {"op":"wait","tab":"demo","text":"Book a demo"} observe until the text appears +// {"op":"fill","tab":"demo","label":"Full name","text":"..."} observe, then actPage on the labelled field +// {"op":"click","tab":"demo","label":"Request demo"} observe, then actPage on the labelled control +// {"op":"move","tab":"demo","x":900,"y":600} moveMouse: show the agent cursor overlay +// {"op":"turnEnded"} hide the agent cursor at the end of a turn +// {"op":"finalize","keep":[{"tab":"demo","status":"deliverable"}]} +// {"op":"exit"} +import fs from "node:fs"; +import net from "node:net"; +import path from "node:path"; + +const state = process.env.BC_STATE, socketPath = process.env.BC_SOCKET; +if (!state || !socketPath) throw new Error("BC_STATE and BC_SOCKET are required; source env.sh"); +const cmdDir = path.join(state, "cmd"), outDir = path.join(state, "out"); +fs.mkdirSync(cmdDir, { recursive: true, mode: 0o700 }); +fs.mkdirSync(outDir, { recursive: true, mode: 0o700 }); + +const socket = net.createConnection(socketPath); +await new Promise((resolve, reject) => { socket.once("connect", resolve); socket.once("error", reject); }); +socket.setEncoding("utf8"); +let next = 0, buffer = ""; +const pending = new Map(); +socket.on("data", chunk => { + buffer += chunk; + let index; + while ((index = buffer.indexOf("\n")) >= 0) { + const message = JSON.parse(buffer.slice(0, index)); buffer = buffer.slice(index + 1); + const waiter = pending.get(message.id); + if (!waiter) continue; + pending.delete(message.id); + message.error ? waiter.reject(new Error(message.error.message)) : waiter.resolve(message.result); + } +}); +socket.on("close", () => { for (const w of pending.values()) w.reject(new Error("socket closed; outcome unknown")); process.exit(3); }); +const call = (method, params = {}) => new Promise((resolve, reject) => { + const id = ++next; + pending.set(id, { resolve, reject }); + socket.write(JSON.stringify({ jsonrpc: "2.0", id, method, params }) + "\n"); +}); +const sleep = ms => new Promise(r => setTimeout(r, ms)); + +const info = await call("host.info"); +if (info.protocolVersion !== 2 || info.extensionProtocol !== "ready") throw new Error(`host not ready: ${JSON.stringify(info)}`); +const extension = await call("getInfo"); +if (extension.protocolVersion !== 2 || extension.pageProtocolVersion !== 2) throw new Error("page protocol v2 required"); +fs.writeFileSync(path.join(outDir, "connected.json"), JSON.stringify({ host: info, extension }, null, 2)); + +const tabs = new Map(); +const observe = async tab => call("observePage", { tabId: tabs.get(tab), controlsOnly: false }); +const find = (observation, label, kind) => { + const action = observation.actions.find(a => a.kind === kind && a.label.trim() === label) + ?? observation.actions.find(a => a.kind === kind && a.label.includes(label)); + if (!action) throw new Error(`no ${kind} action labelled ${label}: ${JSON.stringify(observation.actions.map(a => a.label))}`); + return action; +}; +const ops = { + async open({ tab, url }) { + const created = await call("createTab"); + tabs.set(tab, created.id); + await call("attach", { tabId: created.id }); + const origin = new URL(url).origin; + await call("bindPage", { tabId: created.id, expectedOrigin: origin, allowInsecureLoopback: origin.startsWith("http://127.0.0.1") }); + await call("navigatePage", { tabId: created.id, url }); + return { tabId: created.id }; + }, + async navigate({ tab, url }) { return call("navigatePage", { tabId: tabs.get(tab), url }); }, + async actions({ tab }) { return (await observe(tab)).actions.map(({ kind, label }) => `${kind}:${label}`); }, + async wait({ tab, text, timeoutMs = 15000 }) { + const deadline = Date.now() + timeoutMs; + for (;;) { + try { + const o = await observe(tab); + if (o.text.includes(text)) return { title: o.title, url: o.url, textChars: o.text.length, actions: o.actions.length, excerpt: o.text.slice(0, 300) }; + } catch (error) { if (Date.now() > deadline) throw error; } + if (Date.now() > deadline) throw new Error(`timed out waiting for ${text}`); + await sleep(200); + } + }, + async fill({ tab, label, text }) { + const o = await observe(tab); + return call("actPage", { tabId: tabs.get(tab), snapshot: o.snapshot, actionId: find(o, label, "fill").id, text }); + }, + async click({ tab, label }) { + const o = await observe(tab); + return call("actPage", { tabId: tabs.get(tab), snapshot: o.snapshot, actionId: find(o, label, "click").id }); + }, + async move({ tab, x, y }) { + return call("moveMouse", { tabId: tabs.get(tab), x, y, waitForArrival: true }); + }, + async turnEnded() { return call("turnEnded"); }, + async name({ title }) { return call("nameSession", { name: title }); }, + async tabs() { return call("getTabs"); }, + async finalize({ keep }) { + return call("finalizeTabs", { keep: keep.map(({ tab, status }) => ({ tabId: tabs.get(tab), status })) }); + }, + async exit() { setTimeout(() => { socket.end(); process.exit(0); }, 100); return { exiting: true }; }, +}; + +// On SIGTERM, close this session's agent tabs (finalizeTabs with no keep) before disconnecting. +process.on("SIGTERM", async () => { + try { await Promise.race([call("finalizeTabs", { keep: [] }), sleep(5000)]); } finally { process.exit(0); } +}); + +for (;;) { + const files = fs.readdirSync(cmdDir).filter(f => /^\d+\.json$/.test(f)).sort(); + for (const file of files) { + const command = JSON.parse(fs.readFileSync(path.join(cmdDir, file), "utf8")); + fs.renameSync(path.join(cmdDir, file), path.join(cmdDir, `${file}.taken`)); + let result; + try { result = { ok: true, command, result: await ops[command.op](command) }; } + catch (error) { result = { ok: false, command, error: String(error.message || error) }; } + fs.writeFileSync(path.join(outDir, file), JSON.stringify(result, null, 2)); + } + await sleep(100); +} diff --git a/store/capture/env.sh b/store/capture/env.sh new file mode 100755 index 0000000..26a686f --- /dev/null +++ b/store/capture/env.sh @@ -0,0 +1,20 @@ +# Shared settings for the store capture scripts. Source this file; override any value through the environment. +# BC_TMP must be a private temporary directory. Every path below lives inside it. +: "${BC_TMP:=${TMPDIR:-/tmp}/bc-store-capture}" +BC_TMP=$(mkdir -p "$BC_TMP" && cd "$BC_TMP" && pwd -P) +: "${BC_REPO:=$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")/../.." && pwd -P)}" +# Build tree that holds dist/extension and dist/native-host/host.js (a clean worktree of the release commit). +: "${BC_BUILD:=$BC_TMP/bc-shots-wt}" +: "${BC_PROFILE:=$BC_TMP/bc-shots-profile}" +: "${BC_HOSTDIR:=$BC_TMP/bc-shots-host}" +: "${BC_SOCKET:=$BC_HOSTDIR/bc.sock}" +: "${BC_STATE:=$BC_TMP/bc-shots-state}" +: "${BC_RAW:=$BC_TMP/bc-shots-raw}" +: "${BC_DEMO_PORT:=8765}" +: "${CFT_APP:=$HOME/Library/Caches/ms-playwright/chromium-1228/chrome-mac-arm64/Google Chrome for Testing.app}" +: "${CUA:=$HOME/.local/bin/cua-driver}" +: "${NODE:=$(command -v node)}" +BC_EXTENSION="$BC_BUILD/dist/extension" +BC_HOST_JS="$BC_BUILD/dist/native-host/host.js" +BC_ASSETS="$BC_REPO/store/assets" +export BC_TMP BC_REPO BC_BUILD BC_PROFILE BC_HOSTDIR BC_SOCKET BC_STATE BC_RAW BC_DEMO_PORT CFT_APP CUA NODE BC_EXTENSION BC_HOST_JS BC_ASSETS diff --git a/store/capture/extension-id.py b/store/capture/extension-id.py new file mode 100755 index 0000000..c54f80d --- /dev/null +++ b/store/capture/extension-id.py @@ -0,0 +1,8 @@ +#!/usr/bin/env python3 +"""Print the unpacked extension ID Chrome derives from an absolute extension path.""" +import hashlib +import sys + +path = sys.argv[1].rstrip("/") +digest = hashlib.sha256(path.encode()).hexdigest()[:32] +print("".join(chr(ord("a") + int(c, 16)) for c in digest)) diff --git a/store/capture/launch.sh b/store/capture/launch.sh new file mode 100755 index 0000000..45a2eae --- /dev/null +++ b/store/capture/launch.sh @@ -0,0 +1,61 @@ +#!/usr/bin/env bash +# Provision a fresh Chrome for Testing profile with a private native host, then launch it in the +# background through cua-driver so the frontmost app does not change. Prints the Chrome pid. +set -euo pipefail +source "$(dirname "$0")/env.sh" + +[ -f "$BC_EXTENSION/manifest.json" ] || { echo "missing $BC_EXTENSION; build the release commit first" >&2; exit 1; } +[ ! -e "$BC_PROFILE" ] || { echo "profile $BC_PROFILE already exists; run cleanup.sh" >&2; exit 1; } +umask 077 +mkdir -p "$BC_PROFILE/NativeMessagingHosts" "$BC_HOSTDIR" "$BC_STATE" "$BC_RAW" +chmod 700 "$BC_PROFILE" "$BC_HOSTDIR" "$BC_STATE" "$BC_RAW" +rm -f "$BC_SOCKET" + +EXT_ID=$(python3 "$(dirname "$0")/extension-id.py" "$BC_EXTENSION") +echo "$EXT_ID" > "$BC_STATE/extension-id" + +# Wrapper and manifest follow the same shape as a normal install, but point at a private socket. +cat > "$BC_HOSTDIR/host" < "$BC_PROFILE/NativeMessagingHosts/com.opzero.chrome.json" < "$BC_PROFILE/Default/Preferences" < "$BC_STATE/launch.json" +PID=$(python3 -c 'import json,sys; print(json.load(sys.stdin)["pid"])' <<<"$RESULT") +SUPPRESSED=$(python3 -c 'import json,sys; print(json.load(sys.stdin).get("self_activation_suppressed"))' <<<"$RESULT") +COMMAND=$(ps -ww -p "$PID" -o command=) +case "$COMMAND" in + "$CFT_APP/Contents/MacOS/Google Chrome for Testing"*"--user-data-dir=$BC_PROFILE"*) ;; + *) echo "launched pid $PID is not the expected Chrome for Testing: $COMMAND" >&2; kill "$PID" 2>/dev/null || true; exit 2 ;; +esac +echo "$PID" > "$BC_STATE/chrome.pid" +echo "pid=$PID extension=$EXT_ID self_activation_suppressed=$SUPPRESSED" diff --git a/store/capture/promo/marquee.html b/store/capture/promo/marquee.html new file mode 100644 index 0000000..8ec6ecb --- /dev/null +++ b/store/capture/promo/marquee.html @@ -0,0 +1,34 @@ + + + + + + + +
+

Browser Control

+

Connects your browser tabs to a local AI agent through a native host that you install.

+
    +
  • Opens and groups the agent's own tabs
  • +
  • Reads pages, clicks buttons and fills forms
  • +
  • Leaves finished work in a ✅ tab group
  • +
+
+
+ + diff --git a/store/capture/promo/small.html b/store/capture/promo/small.html new file mode 100644 index 0000000..419ac0e --- /dev/null +++ b/store/capture/promo/small.html @@ -0,0 +1,27 @@ + + + + + + + +
+ +
+

Browser Control

+

Connects your browser tabs to a local AI agent on your computer.

+
+
+
+ + diff --git a/store/capture/render-promo.sh b/store/capture/render-promo.sh new file mode 100755 index 0000000..f9c582b --- /dev/null +++ b/store/capture/render-promo.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash +# Render the promo tiles from promo/*.html with headless Chrome for Testing (via render.mjs) at their exact sizes. +# Inputs: the shipped icon and crops of the real screenshots in store/assets. Output: 24-bit RGB PNGs. +set -euo pipefail +source "$(dirname "$0")/env.sh" +: "${ICON:=$BC_REPO/src/extension/images/icon-128.png}" +work="$BC_RAW/promo"; rm -rf "$work"; mkdir -p "$work" +cp "$(dirname "$0")"/promo/*.html "$work/" +cp "$ICON" "$work/icon.png" +# Real crops: the "✅ Browser Control" tab group from screenshot 3, and the popup and form from screenshot 1. +ffmpeg -v error -y -i "$BC_ASSETS/screenshot-3-deliverable-group.png" -vf "crop=368:28:356:0" "$work/strip.png" +ffmpeg -v error -y -i "$BC_ASSETS/screenshot-1-popup-connected.png" -vf "crop=720:480:560:0" "$work/shot.png" +render() { # name width height output + "$NODE" "$(dirname "$0")/render.mjs" "$CFT_APP/Contents/MacOS/Google Chrome for Testing" "$work/$1.html" "$2" "$3" "$work/$1.png" + ffmpeg -v error -y -i "$work/$1.png" -vf "format=rgb24" -frames:v 1 "$4" + sips -g pixelWidth -g pixelHeight -g hasAlpha "$4" | tail -3 +} +render small 440 280 "$BC_ASSETS/small-promo-440x280.png" +render marquee 1400 560 "$BC_ASSETS/marquee-1400x560.png" diff --git a/store/capture/render.mjs b/store/capture/render.mjs new file mode 100644 index 0000000..f5e6777 --- /dev/null +++ b/store/capture/render.mjs @@ -0,0 +1,47 @@ +#!/usr/bin/env node +// Render one local HTML file to a PNG at an exact CSS-pixel size with headless Chrome for Testing over CDP. +// (This Chrome build's --headless --screenshot flag never writes a file, so the capture goes through CDP.) +// Usage: node render.mjs +import { spawn } from "node:child_process"; +import fs from "node:fs"; +import os from "node:os"; +import path from "node:path"; + +const [binary, html, width, height, out] = process.argv.slice(2); +const w = Number(width), h = Number(height); +const profile = fs.mkdtempSync(path.join(process.env.BC_TMP || os.tmpdir(), "bc-promo-profile-")); +const chrome = spawn(binary, ["--headless", "--use-mock-keychain", "--no-first-run", "--hide-scrollbars", + `--user-data-dir=${profile}`, "--remote-debugging-port=0", `--window-size=${w},${h}`, "about:blank"], { stdio: "ignore" }); +const sleep = ms => new Promise(r => setTimeout(r, ms)); +try { + let port; + for (let i = 0; i < 100 && !port; i++) { + try { port = fs.readFileSync(path.join(profile, "DevToolsActivePort"), "utf8").split("\n")[0]; } catch { await sleep(100); } + } + if (!port) throw new Error("headless Chrome did not start"); + const target = (await (await fetch(`http://127.0.0.1:${port}/json/list`)).json()).find(t => t.type === "page"); + const ws = new WebSocket(target.webSocketDebuggerUrl); + await new Promise((resolve, reject) => { ws.onopen = resolve; ws.onerror = reject; }); + let id = 0; const pending = new Map(), events = []; + ws.onmessage = ({ data }) => { + const m = JSON.parse(data); + if (m.id && pending.has(m.id)) { pending.get(m.id)(m); pending.delete(m.id); } else if (m.method) events.push(m.method); + }; + const send = (method, params = {}) => new Promise((resolve, reject) => { + const n = ++id; pending.set(n, m => m.error ? reject(new Error(m.error.message)) : resolve(m.result)); + ws.send(JSON.stringify({ id: n, method, params })); + }); + await send("Page.enable"); + await send("Emulation.setDeviceMetricsOverride", { width: w, height: h, deviceScaleFactor: 1, mobile: false }); + await send("Page.navigate", { url: `file://${path.resolve(html)}` }); + for (let i = 0; i < 100 && !events.includes("Page.loadEventFired"); i++) await sleep(50); + await send("Runtime.evaluate", { expression: "document.fonts.ready.then(() => true)", awaitPromise: true }); + await sleep(300); + const shot = await send("Page.captureScreenshot", { format: "png", clip: { x: 0, y: 0, width: w, height: h, scale: 1 }, captureBeyondViewport: false }); + fs.writeFileSync(out, Buffer.from(shot.data, "base64")); + ws.close(); +} finally { + chrome.kill("SIGTERM"); + await sleep(500); + fs.rmSync(profile, { recursive: true, force: true }); +} diff --git a/store/capture/serve-demo.sh b/store/capture/serve-demo.sh new file mode 100755 index 0000000..766dc61 --- /dev/null +++ b/store/capture/serve-demo.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +# Serve the fake "Book a demo" page on 127.0.0.1 only. Writes the server pid to $BC_STATE/demo.pid. +set -euo pipefail +source "$(dirname "$0")/env.sh" +mkdir -p "$BC_STATE" +python3 -m http.server "$BC_DEMO_PORT" --bind 127.0.0.1 --directory "$(dirname "$0")/demo" >"$BC_STATE/demo.log" 2>&1 & +echo $! > "$BC_STATE/demo.pid" +sleep 0.5 +curl -fsS "http://127.0.0.1:$BC_DEMO_PORT/" | grep -q "Book a demo" && echo "demo server pid $(cat "$BC_STATE/demo.pid") on 127.0.0.1:$BC_DEMO_PORT" diff --git a/store/capture/shot.sh b/store/capture/shot.sh new file mode 100755 index 0000000..e197ef1 --- /dev/null +++ b/store/capture/shot.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env bash +# Capture this capture's own Chrome window without activating it, then crop the inner 1280x800 rectangle. +# The window is sized to 1300x820 points on a 1x display, so a 10-pixel inset on each edge removes the +# macOS rounded-corner transparency without scaling or padding. Output is 24-bit RGB PNG. +# Usage: shot.sh +set -euo pipefail +source "$(dirname "$0")/env.sh" +out=$1; W=$(cat "$BC_STATE/window.id"); raw="$BC_RAW/$(basename "$out" .png)-raw.png" +screencapture -l "$W" -o -x "$raw" +size=$(sips -g pixelWidth -g pixelHeight "$raw" | awk '/pixel/{printf "%s ", $2}') +if [ "$size" != "1300 820 " ]; then + # A Retina display yields 2600x1640; crop the same inset at 2x, then downscale. + [ "$size" = "2600 1640 " ] || { echo "unexpected capture size $size" >&2; exit 1; } + ffmpeg -v error -y -i "$raw" -vf "crop=2560:1600:20:20,scale=1280:800:flags=lanczos,format=rgb24" -frames:v 1 "$out" +else + ffmpeg -v error -y -i "$raw" -vf "crop=1280:800:10:10,format=rgb24" -frames:v 1 "$out" +fi +sips -g pixelWidth -g pixelHeight -g hasAlpha "$out" | tail -3 diff --git a/store/listing.md b/store/listing.md new file mode 100644 index 0000000..f09aa22 --- /dev/null +++ b/store/listing.md @@ -0,0 +1,308 @@ +# Chrome Web Store listing: Browser Control 0.2.1 + +Paste-ready values for every Chrome Web Store developer dashboard field. The values come from an audit of `src/` at extension version 0.2.1, including commit a88821b, which dropped the `history` and `downloads` permissions. The manifest now requests exactly `alarms`, `debugger`, `nativeMessaging`, `scripting`, `storage`, `tabGroups`, `tabs` and the host permission ``. See [Source audit](#source-audit) for the code behind each claim. + +Character counts are JavaScript string lengths (UTF-16 code units) of the text inside each block. For every field here that equals the number of Unicode code points. Paste the text inside the block, not the fence. + +The public privacy policy is `site/privacy/index.html`, served at . Its Markdown copy is `docs/PRIVACY.md`. The disclosures below match it. See [Consistency with the privacy policy](#consistency-with-the-privacy-policy). + +The site is the Cloudflare Pages project `browser-control` at . The repository is . + +## Store listing tab + +### Item name + +15 / 75 characters. The name contains no Google trademark. + +```text +Browser Control +``` + +### Summary + +The dashboard shows the summary from the package: the `description` field of `src/extension/manifest.json`. It cannot be edited in the dashboard. + +**Recommended: keep the current text.** 108 / 132 characters. It has no trademark, and it already says that the user must install a host. Keeping it avoids a manifest change and a new package. + +```text +Connects browser tabs to a local AI agent on your computer through a native messaging host that you install. +``` + +Optional alternative, only if the manifest is changed anyway. 120 / 132 characters. It also says that the extension is idle without the host. + +```text +Lets an AI agent on your own computer work in your tabs. Does nothing until you install its local native messaging host. +``` + +### Detailed description + +2062 / 16,000 characters. Plain text. No testimonials, and no site list. Keyword check: no word other than common function words appears five or more times. The most frequent terms are "host" and "extension" (4 each); "Chrome", "tab", "agent", "AI", "helper", "page", "install", "control" and "software" (3 each). It names five brands: Google Chrome™, GitHub, Node.js, macOS and Linux. Chrome appears in the "Google Chrome™" compatibility form, so the last line is the trademark attribution. The count includes that line. + +```text +Browser Control lets an AI agent that you run on your own computer do work in Google Chrome™ for you. The extension talks to a small helper program, called a native messaging host, that you install on the same machine. Your agent sends requests to the helper, and the extension carries them out. + +It does nothing on its own. Until the helper is installed, it stays idle and its popup shows "Disconnected". + +WHO IT IS FOR +Developers and people who use AI coding assistants or their own scripts, and who want that software to work in a real window they can watch. + +WHAT IT CAN DO WHEN ASKED +• List your open tabs, or open new ones in the background +• Keep its work in a labeled tab group, apart from yours +• Read the visible text of a page and the buttons, links and fields on it +• Click, type into form fields and attach a local PDF +• Capture screenshots and short recordings +• Send DevTools Protocol commands for advanced automation + +SETUP +1. Add this extension. +2. Download the host program from the project on GitHub and run its installer. It needs Node.js 18 or later on macOS or Linux. Other systems need extra manual configuration. +3. Open the popup and check that it shows "Connected". + +YOU STAY IN CONTROL +• Pause or reload the link from the popup at any time. Pausing ends every session. +• While the debugger is attached, Chrome displays a notice. +• At cleanup, it closes only the tabs it opened and hands back the ones it borrowed. + +PRIVACY +Data goes only to the host on your computer, and from there to the software you connect. Nothing is sent to the maintainers or to any server of theirs. No ads, no analytics, no remote code. Your agent is separate software, so check how it, and any AI service it uses, handles data. + +Sign-in values sent by a local tool are filled straight into verified HTTPS fields. Neither the extension nor the host stores or logs them, and screenshots and page reading are blocked on that page afterwards. + +Source code and issue tracker: https://github.com/opzero1/browser-control + +Google Chrome is a trademark of Google LLC. +``` + +### Category + +**Developer Tools** (in the Productivity group). The users are developers who run local AI coding agents or scripts, and the core feature is a DevTools Protocol bridge. + +### Language + +**English** + +### Graphic assets + +| Asset | Requirement | Source or status | +| --- | --- | --- | +| Store icon | 128 x 128 PNG | `src/extension/images/icon-128.png` (verified 128 x 128 RGBA). The same file is `site/icon.png`. | +| Screenshots | 1 to 5, 1280 x 800 (or 640 x 400), showing the real user experience | TODO(coordinator). Another worker added four 1280 x 800 candidates in `store/assets/`. This change did not review them. Check that each one shows real extension behavior, and that the captions follow the keyword and brand limits. Suggested shots, all from a real session: (1) the popup showing **Connected**; (2) an agent tab in the "Browser Control" tab group with the agent cursor on the page and the Chrome debugging notice visible; (3) the popup after **Pause host**, showing **Paused** and the **Resume host** button; (4) the "✅ Browser Control" group with a kept result tab; (5) the popup showing **Disconnected** with the "Install the native host to connect." hint. Do not stage screens that the extension cannot produce. | +| Small promo tile | 440 x 280 PNG or JPEG | TODO(coordinator). Not produced by this change. | +| Marquee promo tile | 1400 x 560, optional | Not needed. | + +### Additional fields + +| Field | Value | +| --- | --- | +| Official URL / homepage URL | `https://browser-control.pages.dev/` | +| Support URL | `https://browser-control.pages.dev/support/` | +| Mature content | No | + +### Additional instructions for reviewers + +The dashboard field holds 500 characters at most. This text is 493 characters with LF line breaks, or 498 if each line break counts as CRLF. It is plain ASCII. It is copied from `store/reviewer-test-instructions.md`, and it links to the full steps on . + +```text +Idle until its local native messaging host is installed (popup: Disconnected). No account or server is needed. +Test on macOS or Linux with Node.js 18+: +1. Download and unzip https://github.com/opzero1/browser-control/releases/latest/download/chrome-control-skill.zip +2. In that folder run: node scripts/install-native-host.js --extension-id dcnjjnecbhipdbngkhjppkckpkellmld +3. Click Reload host in the popup. It shows Connected. +Full steps: https://browser-control.pages.dev/support/reviewers/ +``` + +## Privacy tab + +### Single purpose description + +489 / 1,000 characters. + +```text +Browser Control lets an AI agent that the user runs on their own computer operate the user's browser tabs, through a native messaging host that the user installs locally. At the agent's request it lists, opens and groups tabs, reads page content, clicks and fills controls, attaches a local PDF, captures screenshots and short recordings, and relays DevTools Protocol commands to the agent's own tabs. It does nothing until the local host is installed, and it sends data only to that host. +``` + +### Permission justifications + +One field for each permission in `src/extension/manifest.json`. Each is 1,000 characters or less. + +#### `alarms` + +572 / 1,000 characters. + +```text +Keeps the connection to the user's locally installed native messaging host working. The service worker (background.ts, NativeTransport.scheduleAlarms) creates two alarms that fire every 30 seconds. The reconnect alarm calls chrome.runtime.connectNative again if the host is not connected and the user has not paused it. The heartbeat alarm pings the host; if the ping fails or times out, all agent sessions are stopped, so control never continues over a dead connection. When the user clicks Pause host in the popup, both alarms are cleared. Alarms never contact a server. +``` + +#### `debugger` + +862 / 1,000 characters. + +```text +Core feature: the user's local AI agent drives its tabs through the Chrome DevTools Protocol. The extension attaches chrome.debugger only to tabs in the agent's session (tabs it opened or claimed). Bundled code sends Page.navigate (stay within the site the agent bound), Page.captureScreenshot (JPEG screenshots and short recordings), DOM.getDocument, DOM.querySelectorAll and DOM.setFileInputFiles (attach a local PDF to an upload field), and Runtime.evaluate with a bundled script (draw the agent cursor if the content script is blocked). The executeCdp request relays other commands from the local agent to its own attached tabs. Target.* and Browser.* commands are refused, and raw commands are refused on origin-bound and privately filled tabs. Events from those tabs go back only to the same local session. Chrome shows its debugging notice while attached. +``` + +#### `nativeMessaging` + +592 / 1,000 characters. + +```text +This is the extension's only communication channel. The service worker calls chrome.runtime.connectNative("com.opzero.chrome") to reach a helper program that the user installs on their own computer. The helper's manifest allows only this extension's ID. The helper accepts requests from the user's local AI agent over a private Unix socket (or an authenticated loopback TCP port) and relays them to the extension as JSON-RPC messages; the extension returns results the same way. Without the helper the extension does nothing, and its popup shows Disconnected. No data goes to a remote server. +``` + +#### `scripting` + +759 / 1,000 characters. + +```text +Runs functions that ship in the package, in the isolated world of the top frame of tabs in the agent's session. They read the page URL, title, visible text and up to 100 clickable or fillable controls (observe); click or fill a control the agent chose from that list (act); verify a file input before a PDF upload; fill sign-in fields with values from a local helper without exposing them in page readings (private fill); and check that no filled password or one-time-code field is on the page before a screenshot. The service worker also injects the bundled content script content-scripts/opzero-chrome.js to draw the agent's cursor, so the user can see where the agent acts. Nothing is injected into tabs outside the agent's session, and no code is fetched. +``` + +#### `storage` + +714 / 1,000 characters. + +```text +Stores small connection and session state in chrome.storage.local: the native host status shown in the popup (NATIVE_HOST_STATUS), whether the user paused the host (NATIVE_HOST_PAUSED), agent tab-group bookkeeping with session IDs, group IDs, tab IDs and group titles (TAB_GROUPS), a random extension instance ID (extensionInstanceId), and after a private fill a per-tab marker with the document ID and the CSS selectors of the filled fields, never the values (PRIVATE_CAPTURE_QUARANTINE:). chrome.storage.session holds the version of a pending update (opChromePendingUpdateVersion) so that the update waits until agent work ends. No page content, screenshots, tab URLs or titles, or credentials are stored. +``` + +#### `tabGroups` + +494 / 1,000 characters. + +```text +Keeps the agent's work visible and separate from the user's own tabs. Every tab the agent opens or claims goes into a tab group for that agent session, titled "Browser Control" by default or with a name the agent sets. Tabs the agent keeps as results move to a shared "✅ Browser Control" group. The extension uses chrome.tabGroups.get and chrome.tabGroups.update to set the title, color and collapsed state, and collapses the group when the session ends. It only updates groups that it created. +``` + +#### `tabs` + +692 / 1,000 characters. + +```text +Lets the user's local agent see and use tabs. getUserTabs calls chrome.tabs.query and returns the ID, URL, title, window, position and group of the user's open tabs, leaving out Chrome internal pages, so the agent can pick one to claim. createTab opens an inactive blank tab in a normal window, and claimUserTab adds a tab the agent picked to its session. The extension also groups and ungroups agent tabs, messages its cursor overlay (chrome.tabs.sendMessage), and, when the agent finishes (finalizeTabs), closes only tabs the agent opened. It listens to chrome.tabs.onUpdated, onRemoved and onReplaced to cancel stale actions after a navigation or when a tab closes. Tab data is not stored. +``` + +### Host permission justification + +For `host_permissions: [""]`. The last sentence also covers `web_accessible_resources`, which has no separate field. 810 / 1,000 characters. + +```text +The user decides which websites their local agent works on, so the sites cannot be listed in advance. Host access is needed to run the bundled page functions with chrome.scripting.executeScript in tabs in the agent's session (read visible text and controls, click, fill, prepare a PDF upload, private fill, and the privacy check before screenshots), and to inject the cursor overlay content script. Access is used only in tabs the agent opened or claimed, never in the user's other tabs, and Chrome internal pages are refused. Before the typed page functions run, the agent must bind the tab to one exact HTTPS origin (or a loopback address with explicit opt-in), and navigation outside that origin is refused. The web-accessible resource images/cursor-chat.svg is only the cursor image that the overlay shows. +``` + +### Remote code + +Answer: **No, I am not using remote code.** + +Justification, 856 / 1,000 characters: + +```text +No. Every script the extension runs is in the package: background.js, popup.js, content-scripts/opzero-chrome.js, and page functions bundled in background.js and injected with chrome.scripting.executeScript({ func }). The extension loads no script from any URL, has no script tag that points outside the package, and never passes strings to eval() or new Function(). Its extension pages use the CSP script-src 'self'. The minified Effect library in the bundles has object methods named eval; they are ordinary methods, not the global eval. For completeness: the executeCdp request can relay a DevTools Protocol command, such as Runtime.evaluate, from the user's own local agent to a page in that agent's tab. That input comes from software on the user's device over native messaging, not from a remote server, and it runs in the page, not in the extension. +``` + +### Data usage + +Google: "Extensions are required to disclose how they handle user data, even when data is processed or stored locally." Browser Control collects nothing for its maintainers, but it reads the data below and passes it to the native messaging host on the same computer. Local handling counts, so the checked categories are the ones that the code handles, even though the data never leaves the device through the extension. + +| Category | Mark | Reason (from the audit) | +| --- | --- | --- | +| Personally identifiable information | Unchecked | No code collects or extracts names, addresses, email addresses or ID numbers. Such details can appear by chance on a page that the agent reads. They are then handled only as Website content, and the privacy policy says so. | +| Health information | Unchecked | No code reads health data. Health details that appear on a page are handled only as Website content. | +| Financial and payment information | Unchecked | No code reads payment, card or transaction data. Financial details that appear on a page are handled only as Website content. | +| Authentication information | **Checked** | Private fill passes passwords and one-time codes from a local program into verified sign-in fields, without storing or logging them (`privateFill`, `background.ts:972-998`; `private-input.ts:25-56`). The `executeCdp` relay (`background.ts:906-926`) can return the cookies of agent tabs. | +| Personal communications | Unchecked | No code reads mail, chat or messaging data. Messages that are visible on a page are handled only as Website content. | +| Location | Unchecked | No code uses geolocation or looks up IP addresses. Location details that a page shows are handled only as Website content. | +| Web history | **Checked** | `getUserTabs` (`background.ts:791-800`) returns the URLs and titles of the user's open tabs. `getTabs`, `createTab` and `claimUserTab` return the URL and title of agent tabs. DevTools events from agent tabs can carry the addresses of pages that the tab loads. | +| User activity | **Checked** | Network and other DevTools events from agent tabs go to the owning local session when the local agent turns those DevTools domains on (`onCDPEvent`, `background.ts:1314-1317`). The extension's own code does not listen to the user's clicks, keystrokes or scrolling. | +| Website content | **Checked** | Visible text, title and control labels (`observePage`), screenshots and recording frames (`capturePage`), and DevTools results (`executeCdp`) from agent tabs. This also covers any personal, health, financial, communication or location details that appear on those pages. | + +### Certifications + +Check all three: + +- [x] I do not sell or transfer user data to third parties, outside of the approved use cases. +- [x] I do not use or transfer user data for purposes that are unrelated to my item's single purpose. +- [x] I do not use or transfer user data to determine creditworthiness or for lending purposes. + +They are true because the extension sends data only to the host on the user's device (`chrome.runtime.connectNative`), has no network code, and uses data only to carry out the local agent's requests. + +### Privacy policy URL + +`https://browser-control.pages.dev/privacy/` + +## Consistency with the privacy policy + +| Listing disclosure | Privacy policy section | +| --- | --- | +| Authentication information (checked) | Table row "Sign-in values", and the section "Private credential fill". Cookies: table row "Chrome DevTools Protocol results and events". | +| Web history (checked) | Table row "Tab details". Page addresses: table row "Chrome DevTools Protocol results and events". | +| User activity (checked) | Table row "Chrome DevTools Protocol results and events" ("other network activity and page events") | +| Website content (checked) | Table rows "Page content", "Screenshots" and "Chrome DevTools Protocol results and events", and the paragraph after the table | +| Personally identifiable, health, financial, personal communications and location (unchecked) | The paragraph after the table: such details are handled "only as part of the website content described above" | +| Storage justification keys | "In the extension's storage": the same five `chrome.storage.local` keys and one `chrome.storage.session` key | +| Remote code: No | "What we do not do", the remote code bullet | +| Certifications | "What we do not do", and the Limited Use sentence | +| Single purpose | "How the data is used" | + +## Source audit + +### Permissions + +Line numbers refer to the files at extension version 0.2.1, after commit a88821b. + +| Permission | Code (file:line) | User-facing feature | Verdict | +| --- | --- | --- | --- | +| `alarms` | `background.ts:391-394` create; `451-452` clear; `1387-1393` `onAlarm` | Automatic reconnect every 30 s, and a heartbeat that stops sessions when the host dies | Keep | +| `debugger` | `background.ts:890` attach; `893`, `1201` detach; `915` `getTargets`; `1118` `sendCommand`; `1314-1317` `onEvent`; `1319-1323` `onDetach` | Navigate, screenshots and recordings, PDF upload, cursor fallback, raw DevTools relay | Keep. It is the core feature. Expect in-depth review. | +| `nativeMessaging` | `background.ts:484` `connectNative("com.opzero.chrome")` | The only channel to the local host | Keep | +| `scripting` | `background.ts:592-595` (`pageControl`), `957-959` (`observePrivateFields`), `993-996` (`fillPrivateFields`), `1223-1227` (content script file) | Observe, act, upload preparation, private fill, cursor overlay | Keep | +| `storage` | `background.ts:136-140` helpers; writes at `240`, `254-265`, `406`, `475`, `991`; `1398` (session); `popup.ts:61`, `79` | Popup status, pause setting, group bookkeeping, private-fill quarantine | Keep | +| `tabGroups` | `background.ts:274-280`, `290-294`, `306-312`, `320-324`, `358`, `876-877` | Labeled agent tab groups and the "✅ Browser Control" group | Keep | +| `tabs` | `background.ts:794` (`query`), `807` (`create`), `784`, `812`, `820`, `827` (`get`), `854` (`remove`), `275`, `288`, `307`, `319` (`group`), `349` (`ungroup`), `1208` (`sendMessage`), `1325-1354` (events, used only to invalidate stale state; nothing is sent to the host) | List and claim tabs, create and close agent tabs, cursor messages | Keep for now. With ``, web-page URLs and titles are readable without `tabs`, but `isControllableUrl` (`background.ts:212-216`) treats a missing URL as controllable. Dropping `tabs` needs that code fixed first, or `chrome://` tabs become claimable. | +| `host_permissions: ` | Needed by every `scripting.executeScript` call above, and by the content script injection | Work on any site that the user chooses | Keep, with justification. Possible narrowing: `https://*/*` plus loopback. The typed page API already requires HTTPS or loopback (`background.ts:707`), but the raw path and the cursor overlay also work on plain HTTP. | +| `web_accessible_resources: images/cursor-chat.svg` on `` | `content-scripts/opzero-chrome.ts:81` | The agent cursor image | Keep or drop. Any site can probe the file to detect the extension. A CSS fallback already exists (`opzero-chrome.ts:82-87`). Consider `use_dynamic_url: true`. | + +Other APIs that need no permission: `chrome.windows.getCurrent` and `getAll` (`background.ts:367-369`), `chrome.runtime` messaging, reload and update events, and `chrome.dom.openOrClosedShadowRoot` (`page-control.ts:28`, `72`). + +The extension sends the host only three kinds of notification: `onControlStopped` (`background.ts:1252`), `onCDPEvent` (`1316`) and `onCDPDetach` (`1322`). + +### Chrome DevTools Protocol methods sent through `chrome.debugger` + +| Method | Code | Feature | +| --- | --- | --- | +| `DOM.getDocument`, `DOM.querySelectorAll`, `DOM.setFileInputFiles` | `background.ts:660`, `662`, `671` | `uploadFile`: attach a local PDF to the file input that the agent chose | +| `Page.navigate` | `background.ts:718` | `navigatePage`: same-origin navigation only | +| `Page.captureScreenshot` (JPEG, quality 80) | `background.ts:759` | `capturePage`: screenshots and recording frames | +| `Runtime.evaluate` (bundled expression) | `background.ts:1189-1193` | Cursor fallback when the content script is unavailable | +| Any method from the local agent, except `Target.*` and `Browser.*` | `background.ts:906-926` | `executeCdp` raw relay. Refused on origin-bound tabs (`911`) and after a private fill (`912-913`). `Target.getTargets` is answered by `chrome.debugger.getTargets`, filtered to the session's tabs (`914-917`). | +| All events from attached session tabs | `background.ts:1314-1317` | Forwarded as `onCDPEvent` to the owning session only (`host.ts:87-93`) | + +### Data flows + +Extension (`background.ts`) → Chrome native messaging (stdio, 4-byte length-prefixed JSON, 1 MB limit toward the extension) → host (`host.ts`) → private Unix socket `~/.opzero-chrome/default.sock` (folder mode 700, socket mode 600), or loopback TCP `127.0.0.1:17365` with a token file → local client (`client.ts` or `transport.ts`) → the user's agent. + +| Data | Source in code | Leaves the device? | +| --- | --- | --- | +| Tab URL, title, IDs, group | `tabInfo`, `background.ts:218-232`; `getTabs` (`780-789`), `getUserTabs` (`791-800`), `createTab`, `claimUserTab` | Only if the local agent sends it | +| Page text, title, control labels | `page-control.ts:217-236` | Only if the local agent sends it | +| Screenshots and recording frames | `background.ts:754-762`; recordings written by `transport.ts:165-211` | Only if the local agent sends it | +| Raw DevTools results and events | `background.ts:906-926`, `1314-1317` | Only if the local agent sends it | +| Private-fill values | `background.ts:972-998`, `private-input.ts:25-56`; the host reduces the reply at `host.ts:66-69` | Typed into the page. The site receives them when the form is submitted. | +| Uploaded PDF | Path only, `background.ts:671`. The extension never reads the bytes. `transport.ts:134-136` reads 5 bytes to check the signature. | To the website, when the page submits the form | +| Agent-typed text and clicks | `page-control.ts:259-277` | To the website, as normal browsing | + +**Remote transmission:** none by the extension. `src/extension` and `src/shared` contain no `fetch`, `XMLHttpRequest`, `WebSocket`, `EventSource`, `sendBeacon` or `importScripts`, and neither do the built bundles in `dist/extension` (rebuilt after a88821b). The only external URL in the extension is the user-clicked "Docs" link in `popup.html:42`. The URLs in the bundles are Effect error-message strings. The extension CSP is `script-src 'self'; connect-src 'self'`. The host, client and transport use only local `net` sockets. Chrome itself contacts websites when the agent navigates or submits. + +## Review risks + +1. **In-depth review is likely.** The item requests `debugger`, `` and `tabs`. Google names `` and `tabs` as causes of longer review. +2. **Raw DevTools relay.** `executeCdp` (`background.ts:906-926`) forwards any method except `Target.*` and `Browser.*`, including `Runtime.evaluate`, `Network.getCookies` and `Storage.*`. A reviewer can treat this as executing code that is not in the package, or as broad data access. The remote-code justification discloses it. To remove the risk, change the code to an allowlist, or block `Runtime.evaluate`, `Runtime.compileScript`, `Page.addScriptToEvaluateOnNewDocument` and cookie and storage methods. +3. **Minified bundles.** `dist/extension/*.js` are minified IIFE bundles of about 190 to 245 KB. Google allows minification, but reviewers must be able to understand the code. Static scanners may flag six `eval(` matches per bundle. They are Effect `Micro` object methods named `eval`, not the global `eval`. Consider `build.minify: false` for the store package, and point reviewers to the public source. +4. **Stale docs outside this change.** `README.md` and `docs/DEVELOPER.md` are current. `scripts/install-chrome-control-skill.sh` still downloads from `opzero1/op-chrome`. `skills/chrome-control/SKILL.md` still says "Opzero Chrome", documents the removed `getUserHistory` call and the `onDownloadChange` notification, and shows `client.js` commands with JSON arguments that `client.ts:13-15` rejects. That file ships inside `chrome-control-skill.zip`, which reviewers download. +5. **Leftover host routing.** `host.ts` still accepts `host.subscribeProfileEvents` and routes `onDownloadChange` to subscribed clients (`host.ts:90`, `135-138`), but the extension no longer sends that notification. The code is unreachable and not part of the extension package. A reviewer who reads the host source may still ask about it. +6. **Windows.** A host started by Chrome on Windows exits unless `OPZERO_CHROME_HOST_TOKEN_FILE` is set (`host.ts:199-206`), and the installer never sets it. `transport.ts:37-40` supports only Unix sockets. Do not claim Windows support. The listing says "Other systems need extra manual configuration". +7. **One profile at a time.** All profiles share one socket, and a second host exits when the socket exists (`host.ts:213`). A reviewer with several profiles sees Disconnected in all but one. The reviewer steps say to use one profile. +8. **Quarantine keys are never removed.** No code removes the `PRIVATE_CAPTURE_QUARANTINE:` keys, so they build up. Tab IDs restart after a browser restart, so a new tab can inherit an old marker, and `executeCdp` then refuses that tab (`background.ts:912-913`). The policy says that these entries stay until uninstall. +9. **DevTools events after a private fill.** If a client turned on a domain such as `Network` before it bound the page, later events, such as a form post, still reach that local session (`background.ts:1314-1317`). The policy states this. +10. **Functionality not visible without the host.** Without the host, reviewers see only a Disconnected popup. The reviewer instructions above and cover this. +11. **Private vulnerability reporting.** The support page sends security reports to `https://github.com/opzero1/browser-control/security/advisories/new`. That form works only if private vulnerability reporting is turned on in the repository settings. diff --git a/store/reviewer-test-instructions.md b/store/reviewer-test-instructions.md new file mode 100644 index 0000000..5cbec22 --- /dev/null +++ b/store/reviewer-test-instructions.md @@ -0,0 +1,163 @@ +# Reviewer test instructions: Browser Control 0.2.1 + +Maintainer note: the public copy of the full steps is `site/support/reviewers/index.html`, served at . When you change any text from "What you need" to the end of this file, make the same change on that page. + +## Short form for the dashboard + +Paste this into the "Additional instructions" field (500 characters at most). It is 493 characters with LF line breaks, or 498 if each line break counts as CRLF. It is plain ASCII. + +```text +Idle until its local native messaging host is installed (popup: Disconnected). No account or server is needed. +Test on macOS or Linux with Node.js 18+: +1. Download and unzip https://github.com/opzero1/browser-control/releases/latest/download/chrome-control-skill.zip +2. In that folder run: node scripts/install-native-host.js --extension-id dcnjjnecbhipdbngkhjppkckpkellmld +3. Click Reload host in the popup. It shows Connected. +Full steps: https://browser-control.pages.dev/support/reviewers/ +``` + +## What you need + +- macOS or Linux, with Google Chrome™ 106 or later (`minimum_chrome_version` in `manifest.json`). Use one Chrome profile with Browser Control turned on. All profiles share one host socket, so a second profile cannot connect at the same time. +- Node.js 18 or later on the `PATH`. Check with `node --version`. The host is a Node.js program. +- `curl` and `unzip`. Both come with macOS. On Debian or Ubuntu: `sudo apt-get install curl unzip`. +- Internet access to `github.com` (to download the helper) and to `https://example.com/` (for the demo page). +- No account, sign-in, API key or server is needed. `ffmpeg` is not needed. +- Windows is not covered by these steps. A host started by Chrome on Windows needs a private token file that the installer does not create. + +## Steps + +### 1. Install the extension and see the idle state + +1. Install Browser Control. +2. Click the Browser Control icon in the toolbar. + +Expected: the popup shows **Disconnected** and "Install the native host to connect." Nothing else happens. The extension has no network code, so it stays idle until a host exists. + +### 2. Download the helper from the GitHub release + +```sh +mkdir -p ~/browser-control-review +cd ~/browser-control-review +curl -fsSL -o chrome-control-skill.zip https://github.com/opzero1/browser-control/releases/latest/download/chrome-control-skill.zip +unzip -o chrome-control-skill.zip -d helper +cd helper +ls +``` + +Expected: the folder contains `SKILL.md`, `native-host/`, `scripts/` and `chunks/`. Run all later commands in this `helper` folder. + +The link downloads `chrome-control-skill.zip` from the latest release, v0.2.1. + +### 3. Install the native messaging host + +```sh +node scripts/install-native-host.js --extension-id dcnjjnecbhipdbngkhjppkckpkellmld +node scripts/check-native-host-manifest.js --json +``` + +Expected: + +- The installer prints `Installed native messaging manifest:` with the manifest path, `Allowed extension origin: chrome-extension://dcnjjnecbhipdbngkhjppkckpkellmld/`, and `Host executable:` with the path of `native-host/opzero-chrome-host`. +- The check prints JSON with `"ok": true` and `"status": "valid"`. + +The manifest is written to `~/Library/Application Support/Google/Chrome/NativeMessagingHosts/com.opzero.chrome.json` on macOS, or `~/.config/google-chrome/NativeMessagingHosts/com.opzero.chrome.json` on Linux. + +### 4. Connect + +1. Open the Browser Control popup. +2. Click **Reload host**. + +Expected: the popup shows **Connected** and "Connected to the local agent host." If it still shows Disconnected, wait 30 seconds (the extension retries automatically) or click **Reload host** again. + +### 5. Check the connection from the command line + +```sh +node native-host/client.js ping +node native-host/client.js getInfo +``` + +Expected: + +- `ping` prints `{"jsonrpc":"2.0","id":1,"result":"pong"}`. The answer comes from the extension through the host. +- `getInfo` prints one JSON line with `"version":"0.2.1"`, `"protocolVersion":2`, `"pageProtocolVersion":2` and `"extensionId":"dcnjjnecbhipdbngkhjppkckpkellmld"`. + +If `ping` reports `Extension protocol v2 is not ready`, wait a few seconds and run it again. The host checks the extension for up to 10 seconds after it starts. + +### 6. List open tabs (read only) + +The client accepts request data only on standard input: + +```sh +echo '{"jsonrpc":"2.0","id":1,"method":"getUserTabs","params":{}}' | node native-host/client.js --stdio +``` + +Expected: one JSON line whose `result` lists your open tabs with `id`, `title`, `url`, `windowId` and `groupId`. Chrome internal pages, such as `chrome://extensions`, are left out. + +### 7. Open and read a tab + +Save this file as `review-demo.js` in the `helper` folder: + +```js +const os = require("node:os"); +const path = require("node:path"); +const { ChromeTransport } = require("./native-host/transport.js"); + +const socket = process.env.OPZERO_CHROME_HOST_SOCKET || path.join(os.homedir(), ".opzero-chrome", "default.sock"); + +(async () => { + const browser = await ChromeTransport.connect(socket); + try { + const page = await browser.open("https://example.com/"); + const snapshot = await browser.waitFor(page, { text: "Example Domain" }, 15000); + console.log(JSON.stringify({ url: snapshot.url, title: snapshot.title, text: snapshot.text.slice(0, 200), actions: snapshot.actions }, null, 2)); + console.log("The tab stays open for 20 seconds. Click it in the \"Browser Control\" tab group to watch."); + await new Promise((resolve) => setTimeout(resolve, 20000)); + } finally { + await browser.close(); + } +})().catch((error) => { + console.error(error.message); + process.exit(1); +}); +``` + +Then run: + +```sh +node review-demo.js +``` + +Expected: + +1. A new background tab opens in a tab group named **Browser Control**. +2. Chrome shows its notice that Browser Control is debugging the browser. +3. The script prints JSON with `"url": "https://example.com/"`, the page title, the start of the visible text, and an `actions` list with the page's link. +4. After 20 seconds the script ends and the extension closes the tab that it opened. Your own tabs stay as they were. + +This uses the same client library (`native-host/transport.js`) that agent tools use. It opens the tab, attaches the debugger, binds the tab to `https://example.com`, navigates, and reads the page. + +### 8. Pause and resume + +1. In the popup, click **Pause host**. The popup shows **Paused**, and the button changes to **Resume host**. +2. Run `node native-host/client.js ping`. It fails with `Private client stopped; outcome may be unknown; do not replay`, because pausing stops the host. +3. Click **Resume host**. The popup shows **Connected** again, and `ping` prints `pong`. + +### 9. Clean up + +1. Remove the extension on `chrome://extensions`. +2. Remove the host manifest and the socket folder: + - macOS: `rm "$HOME/Library/Application Support/Google/Chrome/NativeMessagingHosts/com.opzero.chrome.json"` + - Linux: `rm "$HOME/.config/google-chrome/NativeMessagingHosts/com.opzero.chrome.json"` + - Both: `rm -r "$HOME/.opzero-chrome" ~/browser-control-review` + +## Where to look in the code + +| Behavior | Source | +| --- | --- | +| Connection, reconnect and heartbeat | `src/extension/background.ts`, `NativeTransport` | +| Request handlers (`getUserTabs`, `createTab`, `observePage`, `capturePage`, `executeCdp` and others) | `src/extension/background.ts`, the `api` object | +| Page reading and actions | `src/extension/page-control.ts` | +| Private credential fill | `src/extension/private-input.ts` | +| Cursor overlay | `src/extension/content-scripts/opzero-chrome.ts` | +| Native messaging host | `src/native-host/host.ts` | +| Command-line client and client library | `src/native-host/client.ts`, `src/native-host/transport.ts` | From 4378daa38d572be102e611425ddabc02b76fb147 Mon Sep 17 00:00:00 2001 From: afif Date: Mon, 28 Sep 2026 15:25:54 +0800 Subject: [PATCH 10/17] Recover the native host after a crash and stop reporting a missing host as connected A host killed without cleanup left its socket file behind, and every respawn refused to start, so the extension never reconnected. The host now removes the file only when a connection to it is refused. connectNative returns a port even when no host is installed, so the popup showed Connected for a missing host. The extension now reports Connecting until the host sends its first message. The popup also opens chrome://extensions through the tabs API, which a plain link cannot. --- skills/chrome-control/native-host/host.js | 40 ++++++++++++++++++--- src/extension/background.ts | 20 ++++++++--- src/extension/popup.html | 2 +- src/extension/popup.ts | 17 +++++++-- src/native-host/host.ts | 28 +++++++++++++-- tests/security/host.test.ts | 36 +++++++++++++++++++ tests/security/native-host-controls.test.ts | 34 +++++++++++++++--- 7 files changed, 158 insertions(+), 19 deletions(-) diff --git a/skills/chrome-control/native-host/host.js b/skills/chrome-control/native-host/host.js index 6da2997..6504d3f 100644 --- a/skills/chrome-control/native-host/host.js +++ b/skills/chrome-control/native-host/host.js @@ -274,16 +274,46 @@ try { }); const stat = node_fs.default.lstatSync(directory); if (!stat.isDirectory() || stat.uid !== node_process.default.getuid?.() || (stat.mode & 63) !== 0) throw new Error("private socket directory required"); - if (node_fs.default.existsSync(socketPath)) throw new Error("endpoint exists"); - server.listen(socketPath, () => { - ownsSocket = true; - node_fs.default.chmodSync(socketPath, 384); - }); + if (!node_fs.default.existsSync(socketPath)) listenUnix(); + else { + const existing = node_fs.default.lstatSync(socketPath); + if (!existing.isSocket() || existing.uid !== node_process.default.getuid?.()) throw new Error("endpoint exists"); + reclaimStaleSocket(); + } } } catch { + refuseEndpoint(); +} +function refuseEndpoint() { node_process.default.stderr.write("Native endpoint setup refused; use a private directory or authenticated TCP\n"); shutdown(1); } +function listenUnix() { + server.listen(socketPath, () => { + ownsSocket = true; + node_fs.default.chmodSync(socketPath, 384); + }); +} +function reclaimStaleSocket() { + const probe = node_net.default.connect(socketPath); + probe.once("connect", () => { + probe.destroy(); + refuseEndpoint(); + }); + probe.once("error", (probeError) => { + if (probeError.code !== "ECONNREFUSED") { + refuseEndpoint(); + return; + } + try { + node_fs.default.unlinkSync(socketPath); + } catch { + refuseEndpoint(); + return; + } + listenUnix(); + }); +} var buffer = Buffer.alloc(0); node_process.default.stdin.on("data", (chunk) => { buffer = Buffer.concat([buffer, Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)]); diff --git a/src/extension/background.ts b/src/extension/background.ts index 653f428..ad63fef 100644 --- a/src/extension/background.ts +++ b/src/extension/background.ts @@ -482,11 +482,14 @@ class NativeTransport { if (this.paused || this.port) return; try { this.port = chrome.runtime.connectNative(this.hostName); - this.connected = true; - this.reconnectAttempt = 0; - this.setStatus("connected").catch(() => undefined); + this.connected = false; + this.setStatus("connecting").catch(() => undefined); const port = this.port; - port.onMessage.addListener((message) => { if (this.port === port) this.onMessage(message); }); + port.onMessage.addListener((message) => { + if (this.port !== port) return; + this.markConnected(); + this.onMessage(message); + }); port.onDisconnect.addListener(() => { if (this.port === port) this.onDisconnect(); }); } catch (error) { this.connected = false; @@ -499,6 +502,15 @@ class NativeTransport { } } + // connectNative returns a port even when no host is installed, so only a + // message from the host proves that it is running. + markConnected() { + if (this.connected) return; + this.connected = true; + this.reconnectAttempt = 0; + this.setStatus("connected").catch(() => undefined); + } + onDisconnect() { const message = chrome.runtime.lastError?.message || "Native host disconnected"; this.connected = false; diff --git a/src/extension/popup.html b/src/extension/popup.html index 19dfed0..e103995 100644 --- a/src/extension/popup.html +++ b/src/extension/popup.html @@ -40,7 +40,7 @@

Browser Control

Version v0.1.0
diff --git a/src/extension/popup.ts b/src/extension/popup.ts index be41d1c..c885411 100644 --- a/src/extension/popup.ts +++ b/src/extension/popup.ts @@ -9,6 +9,7 @@ const errorText = document.getElementById("error-text") as HTMLElement; const version = document.getElementById("version") as HTMLElement; const reloadButton = document.getElementById("reload-host") as HTMLButtonElement; const pauseButton = document.getElementById("pause-host") as HTMLButtonElement; +const extensionsLink = document.getElementById("open-extensions") as HTMLAnchorElement; version.textContent = `Version v${chrome.runtime.getManifest().version}`; @@ -24,13 +25,19 @@ function renderStatus(status: Partial = {}) { currentState = state; const connected = state === "connected"; const paused = state === "paused"; - statusPill.className = `pill ${connected ? "connected" : paused ? "paused" : "disconnected"}`; - statusPill.textContent = connected ? "Connected" : paused ? "Paused" : "Disconnected"; + const connecting = state === "connecting"; + const hostMissing = !status.error || /not found/i.test(status.error); + statusPill.className = `pill ${connected ? "connected" : paused ? "paused" : connecting ? "pending" : "disconnected"}`; + statusPill.textContent = connected ? "Connected" : paused ? "Paused" : connecting ? "Connecting" : "Disconnected"; summary.textContent = connected ? "Connected to the local agent host." : paused ? "Native host paused. Resume to reconnect." - : "Install the native host to connect."; + : connecting + ? "Starting the local native host..." + : hostMissing + ? "Install the native host to connect." + : "Native host stopped. It restarts automatically, or click Reload host."; hostName.textContent = status.hostName || "-"; lastChecked.textContent = formatTime(status.lastChecked); errorText.textContent = status.error || "-"; @@ -75,6 +82,10 @@ function refreshStatus() { reloadButton.addEventListener("click", () => sendControl("RELOAD_NATIVE_HOST")); pauseButton.addEventListener("click", () => sendControl(currentState === "paused" ? "RESUME_NATIVE_HOST" : "PAUSE_NATIVE_HOST")); +extensionsLink.addEventListener("click", (event) => { + event.preventDefault(); + void chrome.tabs.create({ url: `chrome://extensions/?id=${chrome.runtime.id}` }); +}); chrome.storage.onChanged.addListener((changes, area) => { const status = parseWithSchema(NativeHostStatusSchema, changes.NATIVE_HOST_STATUS?.newValue); diff --git a/src/native-host/host.ts b/src/native-host/host.ts index f4df43a..3f79820 100755 --- a/src/native-host/host.ts +++ b/src/native-host/host.ts @@ -210,14 +210,38 @@ try { fs.mkdirSync(directory, { recursive: true, mode: 0o700 }); const stat = fs.lstatSync(directory); if (!stat.isDirectory() || stat.uid !== process.getuid?.() || (stat.mode & 0o077) !== 0) throw new Error("private socket directory required"); - if (fs.existsSync(socketPath)) throw new Error("endpoint exists"); - server.listen(socketPath, () => { ownsSocket = true; fs.chmodSync(socketPath, 0o600); }); + if (!fs.existsSync(socketPath)) listenUnix(); + else { + const existing = fs.lstatSync(socketPath); + if (!existing.isSocket() || existing.uid !== process.getuid?.()) throw new Error("endpoint exists"); + reclaimStaleSocket(); + } } } catch { + refuseEndpoint(); +} + +function refuseEndpoint() { process.stderr.write("Native endpoint setup refused; use a private directory or authenticated TCP\n"); shutdown(1); } +function listenUnix() { + server.listen(socketPath, () => { ownsSocket = true; fs.chmodSync(socketPath, 0o600); }); +} + +// A host killed without cleanup leaves its socket file behind. Only a +// refused connection proves no live host owns it; anything else is left alone. +function reclaimStaleSocket() { + const probe = net.connect(socketPath); + probe.once("connect", () => { probe.destroy(); refuseEndpoint(); }); + probe.once("error", (probeError: NodeJS.ErrnoException) => { + if (probeError.code !== "ECONNREFUSED") { refuseEndpoint(); return; } + try { fs.unlinkSync(socketPath); } catch { refuseEndpoint(); return; } + listenUnix(); + }); +} + let buffer = Buffer.alloc(0); process.stdin.on("data", chunk => { buffer = Buffer.concat([buffer, Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)]); diff --git a/tests/security/host.test.ts b/tests/security/host.test.ts index c1461de..b661d2d 100644 --- a/tests/security/host.test.ts +++ b/tests/security/host.test.ts @@ -158,6 +158,42 @@ it("protects Unix endpoint permissions and never steals a running endpoint", asy await vi.waitFor(() => expect(a.messages[0]?.result).toBe("pong")); }); +it("reclaims the stale endpoint of a host that was killed without cleanup", async () => { + const h = await host(); + const exited = new Promise(resolve => h.child.once("exit", resolve)); + h.child.kill("SIGKILL"); + await exited; + expect(fs.lstatSync(h.endpoint).isSocket()).toBe(true); + const child = spawn(process.execPath, ["dist/native-host/host.js"], { + env: { ...process.env, OPZERO_CHROME_HOST_SOCKET: h.endpoint }, stdio: ["pipe", "ignore", "pipe"] + }); + cleanup.push(() => child.kill()); + await vi.waitFor(async () => { + const pong = await new Promise((resolve, reject) => { + const socket = net.connect(h.endpoint); + socket.setEncoding("utf8"); + socket.once("error", reject); + socket.once("connect", () => socket.write(`${JSON.stringify({ jsonrpc: "2.0", id: 1, method: "host.ping" })}\n`)); + socket.once("data", chunk => { socket.destroy(); resolve(JSON.parse(String(chunk)).result); }); + }); + expect(pong).toBe("pong"); + }); + expect(fs.statSync(h.endpoint).mode & 0o777).toBe(0o600); +}); + +it("refuses an endpoint path that holds something other than a socket", async () => { + const directory = testTemp(); + cleanup.push(() => fs.rmSync(directory, { recursive: true, force: true })); + const endpoint = path.join(directory, "s"); + fs.writeFileSync(endpoint, "not a socket"); + const child = spawn(process.execPath, ["dist/native-host/host.js"], { + env: { ...process.env, OPZERO_CHROME_HOST_SOCKET: endpoint }, stdio: ["pipe", "ignore", "pipe"] + }); + cleanup.push(() => child.kill()); + expect(await new Promise(resolve => child.on("exit", resolve))).toBe(1); + expect(fs.readFileSync(endpoint, "utf8")).toBe("not a socket"); +}); + it.each(["dispatch", "release"])("removes its own endpoint when the native output pipe breaks during %s", async mode => { const h = await host(); const a = await h.connect(); a.request(1, "host.info"); diff --git a/tests/security/native-host-controls.test.ts b/tests/security/native-host-controls.test.ts index c528a5b..f0e62bb 100644 --- a/tests/security/native-host-controls.test.ts +++ b/tests/security/native-host-controls.test.ts @@ -2,6 +2,11 @@ import { expect, it, vi } from "vitest"; import { background } from "../support/background"; const reconnectAlarm = { name: "native-transport-reconnect:com.opzero.chrome" }; +const hostProbe = { jsonrpc: "2.0", id: "protocol:test", method: "getInfo", params: {} }; + +function hostState(b: Awaited>) { + return (b.storage.NATIVE_HOST_STATUS as { state: string; error?: string } | undefined); +} it("keeps the native host paused across reconnect alarms and worker restarts until resumed", async () => { const b = await background(); @@ -22,9 +27,11 @@ it("keeps the native host paused across reconnect alarms and worker restarts unt expect((await restarted.popup("GET_NATIVE_HOST_STATUS")).status.state).toBe("paused"); const resumed = await restarted.popup("RESUME_NATIVE_HOST"); - expect(resumed.status.state).toBe("connected"); + expect(resumed.status.state).toBe("connecting"); expect(restarted.chrome.runtime.connectNative).toHaveBeenCalledTimes(1); expect(restarted.storage.NATIVE_HOST_PAUSED).toBe(false); + restarted.port.onMessage.emit(hostProbe); + await vi.waitFor(() => expect(hostState(restarted)?.state).toBe("connected")); }); it("reloads the native host on a fresh port and reconnects after the host exits", async () => { @@ -34,15 +41,34 @@ it("reloads the native host on a fresh port and reconnects after the host exits" expect((await b.rpc("bindPage", owner)).error).toBeUndefined(); const reloaded = await b.popup("RELOAD_NATIVE_HOST"); - expect(reloaded.status.state).toBe("connected"); + expect(reloaded.status.state).toBe("connecting"); expect(b.port.disconnect).toHaveBeenCalledTimes(1); expect(b.chrome.runtime.connectNative).toHaveBeenCalledTimes(2); expect(b.storage.NATIVE_HOST_PAUSED).toBe(false); expect((await b.rpc("bindPage", owner)).error).toBeDefined(); + await vi.waitFor(() => expect(hostState(b)?.state).toBe("connected")); b.port.onDisconnect.emit(); - await vi.waitFor(() => expect((b.storage.NATIVE_HOST_STATUS as { state: string }).state).toBe("disconnected")); + await vi.waitFor(() => expect(hostState(b)?.state).toBe("disconnected")); b.chrome.alarms.onAlarm.emit(reconnectAlarm); expect(b.chrome.runtime.connectNative).toHaveBeenCalledTimes(3); - await vi.waitFor(() => expect((b.storage.NATIVE_HOST_STATUS as { state: string }).state).toBe("connected")); + await vi.waitFor(() => expect(hostState(b)?.state).toBe("connecting")); + b.port.onMessage.emit(hostProbe); + await vi.waitFor(() => expect(hostState(b)?.state).toBe("connected")); +}); + +it("never reports a missing native host as connected", async () => { + const b = await background(); + const states: string[] = []; + const set = b.chrome.storage.local.set; + b.chrome.storage.local.set = (value: Record, cb: Function) => { + if (value.NATIVE_HOST_STATUS) states.push(value.NATIVE_HOST_STATUS.state); + set(value, cb); + }; + const reloaded = await b.popup("RELOAD_NATIVE_HOST"); + expect(reloaded.status.state).toBe("connecting"); + (b.chrome.runtime as { lastError?: { message: string } }).lastError = { message: "Specified native messaging host not found." }; + b.port.onDisconnect.emit(); + await vi.waitFor(() => expect(hostState(b)).toMatchObject({ state: "disconnected", error: "Specified native messaging host not found." })); + expect(states).not.toContain("connected"); }); From 60e4db964e97a4a8b5de25476c27d912b9b546e8 Mon Sep 17 00:00:00 2001 From: afif Date: Mon, 28 Sep 2026 15:47:18 +0800 Subject: [PATCH 11/17] Host the 0.2.1 helper on the website for reviewers The latest GitHub release is still 0.1.3, so the reviewer steps would download a host that predates protocol v2. The site now serves the freshly built chrome-control-skill.zip, and RELEASE.md copies it in before each deploy. --- .gitignore | 1 + docs/RELEASE.md | 7 +++++-- site/index.html | 4 ++-- site/support/reviewers/index.html | 8 ++++---- store/listing.md | 4 ++-- store/reviewer-test-instructions.md | 12 ++++++------ 6 files changed, 20 insertions(+), 16 deletions(-) diff --git a/.gitignore b/.gitignore index 07b540e..64cbbac 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ dist/ scripts/extension-id.json experiments/artifacts/ experiments/chrome-jev/ready.json +site/download/ diff --git a/docs/RELEASE.md b/docs/RELEASE.md index ba1f2c9..6007012 100644 --- a/docs/RELEASE.md +++ b/docs/RELEASE.md @@ -35,13 +35,16 @@ The `Release` workflow creates the GitHub Release with `browser-control-extensio ## 3. Deploy the website -Deploy when anything under `site/` changed: +Deploy after every release, and whenever anything under `site/` changes. The site also hosts the helper zip that the homepage and the reviewer steps download, so copy the freshly built one in first. `site/download/` is not committed. ```sh +pnpm run build +mkdir -p site/download +cp dist/release/chrome-control-skill.zip site/download/ npx wrangler pages deploy site --project-name browser-control --branch main ``` -Check that `/`, `/privacy/`, `/support/` and `/support/reviewers/` return HTTP 200. Keep the `google-site-verification` meta tag in `site/index.html`. It proves ownership of the site in Google Search Console, which the listing's official URL requires. Cloudflare Pages redirects `.html` URLs to extensionless ones, so the HTML-file verification method does not work on this site. +Check that `/`, `/privacy/`, `/support/`, `/support/reviewers/` and `/download/chrome-control-skill.zip` return HTTP 200. Keep the `google-site-verification` meta tag in `site/index.html`. It proves ownership of the site in Google Search Console, which the listing's official URL requires. Cloudflare Pages redirects `.html` URLs to extensionless ones, so the HTML-file verification method does not work on this site. ## 4. Upload to the Chrome Web Store diff --git a/site/index.html b/site/index.html index 2b49ca1..fcf8a2e 100644 --- a/site/index.html +++ b/site/index.html @@ -73,8 +73,8 @@

How it works

Install

  1. Install Browser Control from the Chrome Web Store.
  2. -
  3. Install the local native messaging host and the agent tooling from github.com/opzero1/browser-control. Both come in the chrome-control-skill.zip file of the latest release. The host needs Node.js 18 or later, and its default setup is for macOS and Linux. For example: -
    curl -fsSL -o chrome-control-skill.zip https://github.com/opzero1/browser-control/releases/latest/download/chrome-control-skill.zip
    +      
  4. Install the local native messaging host and the agent tooling. Both come in chrome-control-skill.zip, which this website hosts and which is built from the source at github.com/opzero1/browser-control. The host needs Node.js 18 or later, and its default setup is for macOS and Linux. For example: +
    curl -fsSL -o chrome-control-skill.zip https://browser-control.pages.dev/download/chrome-control-skill.zip
     unzip chrome-control-skill.zip -d browser-control-helper
     cd browser-control-helper
     node scripts/install-native-host.js --extension-id dcnjjnecbhipdbngkhjppkckpkellmld
    diff --git a/site/support/reviewers/index.html b/site/support/reviewers/index.html index 4fbc00b..4867f6a 100644 --- a/site/support/reviewers/index.html +++ b/site/support/reviewers/index.html @@ -38,7 +38,7 @@

    What you need

  5. macOS or Linux, with Google Chrome™ 106 or later (minimum_chrome_version in manifest.json). Use one Chrome profile with Browser Control turned on. All profiles share one host socket, so a second profile cannot connect at the same time.
  6. Node.js 18 or later on the PATH. Check with node --version. The host is a Node.js program.
  7. curl and unzip. Both come with macOS. On Debian or Ubuntu: sudo apt-get install curl unzip.
  8. -
  9. Internet access to github.com (to download the helper) and to https://example.com/ (for the demo page).
  10. +
  11. Internet access to browser-control.pages.dev (to download the helper) and to https://example.com/ (for the demo page).
  12. No account, sign-in, API key or server is needed. ffmpeg is not needed.
  13. Windows is not covered by these steps. A host started by Chrome on Windows needs a private token file that the installer does not create.
  14. @@ -49,15 +49,15 @@

    1. Install the extension and see the idle state

  15. Click the Browser Control icon in the toolbar.

Expected: the popup shows Disconnected and "Install the native host to connect." Nothing else happens. The extension has no network code, so it stays idle until a host exists.

-

2. Download the helper from the GitHub release

+

2. Download the helper

mkdir -p ~/browser-control-review
 cd ~/browser-control-review
-curl -fsSL -o chrome-control-skill.zip https://github.com/opzero1/browser-control/releases/latest/download/chrome-control-skill.zip
+curl -fsSL -o chrome-control-skill.zip https://browser-control.pages.dev/download/chrome-control-skill.zip
 unzip -o chrome-control-skill.zip -d helper
 cd helper
 ls

Expected: the folder contains SKILL.md, native-host/, scripts/ and chunks/. Run all later commands in this helper folder.

-

The link downloads chrome-control-skill.zip from the latest release, v0.2.1.

+

The link downloads chrome-control-skill.zip for version 0.2.1 from this website. The same file is built from the source at github.com/opzero1/browser-control.

3. Install the native messaging host

node scripts/install-native-host.js --extension-id dcnjjnecbhipdbngkhjppkckpkellmld
 node scripts/check-native-host-manifest.js --json
diff --git a/store/listing.md b/store/listing.md index f09aa22..49a09a2 100644 --- a/store/listing.md +++ b/store/listing.md @@ -101,12 +101,12 @@ Google Chrome is a trademark of Google LLC. ### Additional instructions for reviewers -The dashboard field holds 500 characters at most. This text is 493 characters with LF line breaks, or 498 if each line break counts as CRLF. It is plain ASCII. It is copied from `store/reviewer-test-instructions.md`, and it links to the full steps on . +The dashboard field holds 500 characters at most. This text is 468 characters with LF line breaks, or 473 if each line break counts as CRLF. It is plain ASCII. It is copied from `store/reviewer-test-instructions.md`, and it links to the full steps on . ```text Idle until its local native messaging host is installed (popup: Disconnected). No account or server is needed. Test on macOS or Linux with Node.js 18+: -1. Download and unzip https://github.com/opzero1/browser-control/releases/latest/download/chrome-control-skill.zip +1. Download and unzip https://browser-control.pages.dev/download/chrome-control-skill.zip 2. In that folder run: node scripts/install-native-host.js --extension-id dcnjjnecbhipdbngkhjppkckpkellmld 3. Click Reload host in the popup. It shows Connected. Full steps: https://browser-control.pages.dev/support/reviewers/ diff --git a/store/reviewer-test-instructions.md b/store/reviewer-test-instructions.md index 5cbec22..abf52bb 100644 --- a/store/reviewer-test-instructions.md +++ b/store/reviewer-test-instructions.md @@ -4,12 +4,12 @@ Maintainer note: the public copy of the full steps is `site/support/reviewers/in ## Short form for the dashboard -Paste this into the "Additional instructions" field (500 characters at most). It is 493 characters with LF line breaks, or 498 if each line break counts as CRLF. It is plain ASCII. +Paste this into the "Additional instructions" field (500 characters at most). It is 468 characters with LF line breaks, or 473 if each line break counts as CRLF. It is plain ASCII. ```text Idle until its local native messaging host is installed (popup: Disconnected). No account or server is needed. Test on macOS or Linux with Node.js 18+: -1. Download and unzip https://github.com/opzero1/browser-control/releases/latest/download/chrome-control-skill.zip +1. Download and unzip https://browser-control.pages.dev/download/chrome-control-skill.zip 2. In that folder run: node scripts/install-native-host.js --extension-id dcnjjnecbhipdbngkhjppkckpkellmld 3. Click Reload host in the popup. It shows Connected. Full steps: https://browser-control.pages.dev/support/reviewers/ @@ -20,7 +20,7 @@ Full steps: https://browser-control.pages.dev/support/reviewers/ - macOS or Linux, with Google Chrome™ 106 or later (`minimum_chrome_version` in `manifest.json`). Use one Chrome profile with Browser Control turned on. All profiles share one host socket, so a second profile cannot connect at the same time. - Node.js 18 or later on the `PATH`. Check with `node --version`. The host is a Node.js program. - `curl` and `unzip`. Both come with macOS. On Debian or Ubuntu: `sudo apt-get install curl unzip`. -- Internet access to `github.com` (to download the helper) and to `https://example.com/` (for the demo page). +- Internet access to `browser-control.pages.dev` (to download the helper) and to `https://example.com/` (for the demo page). - No account, sign-in, API key or server is needed. `ffmpeg` is not needed. - Windows is not covered by these steps. A host started by Chrome on Windows needs a private token file that the installer does not create. @@ -33,12 +33,12 @@ Full steps: https://browser-control.pages.dev/support/reviewers/ Expected: the popup shows **Disconnected** and "Install the native host to connect." Nothing else happens. The extension has no network code, so it stays idle until a host exists. -### 2. Download the helper from the GitHub release +### 2. Download the helper ```sh mkdir -p ~/browser-control-review cd ~/browser-control-review -curl -fsSL -o chrome-control-skill.zip https://github.com/opzero1/browser-control/releases/latest/download/chrome-control-skill.zip +curl -fsSL -o chrome-control-skill.zip https://browser-control.pages.dev/download/chrome-control-skill.zip unzip -o chrome-control-skill.zip -d helper cd helper ls @@ -46,7 +46,7 @@ ls Expected: the folder contains `SKILL.md`, `native-host/`, `scripts/` and `chunks/`. Run all later commands in this `helper` folder. -The link downloads `chrome-control-skill.zip` from the latest release, v0.2.1. +The link downloads `chrome-control-skill.zip` for version 0.2.1 from this website. The same file is built from the source at [github.com/opzero1/browser-control](https://github.com/opzero1/browser-control). ### 3. Install the native messaging host From 7724087e368eec4abc5b2cbb6f56c8c5359316b7 Mon Sep 17 00:00:00 2001 From: afif Date: Mon, 28 Sep 2026 15:51:45 +0800 Subject: [PATCH 12/17] Point the store description at the website download --- store/listing.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/store/listing.md b/store/listing.md index 49a09a2..19d1feb 100644 --- a/store/listing.md +++ b/store/listing.md @@ -36,7 +36,7 @@ Lets an AI agent on your own computer work in your tabs. Does nothing until you ### Detailed description -2062 / 16,000 characters. Plain text. No testimonials, and no site list. Keyword check: no word other than common function words appears five or more times. The most frequent terms are "host" and "extension" (4 each); "Chrome", "tab", "agent", "AI", "helper", "page", "install", "control" and "software" (3 each). It names five brands: Google Chrome™, GitHub, Node.js, macOS and Linux. Chrome appears in the "Google Chrome™" compatibility form, so the last line is the trademark attribution. The count includes that line. +2066 / 16,000 characters. Plain text. No testimonials, and no site list. Keyword check: no word other than common function words appears five or more times. The most frequent terms are "host" and "extension" (4 each); "Chrome", "tab", "agent", "AI", "helper", "page", "install", "control" and "software" (3 each). It names five brands: Google Chrome™, GitHub, Node.js, macOS and Linux. Chrome appears in the "Google Chrome™" compatibility form, so the last line is the trademark attribution. The count includes that line. ```text Browser Control lets an AI agent that you run on your own computer do work in Google Chrome™ for you. The extension talks to a small helper program, called a native messaging host, that you install on the same machine. Your agent sends requests to the helper, and the extension carries them out. @@ -56,7 +56,7 @@ WHAT IT CAN DO WHEN ASKED SETUP 1. Add this extension. -2. Download the host program from the project on GitHub and run its installer. It needs Node.js 18 or later on macOS or Linux. Other systems need extra manual configuration. +2. Download the host program from browser-control.pages.dev and run its installer. It needs Node.js 18 or later on macOS or Linux. Other systems need extra manual configuration. 3. Open the popup and check that it shows "Connected". YOU STAY IN CONTROL From 4e0a4035bc14a8cfca615e19dbd7c88fba59cffc Mon Sep 17 00:00:00 2001 From: afif Date: Mon, 28 Sep 2026 16:09:56 +0800 Subject: [PATCH 13/17] Serialize stale-socket recovery and drop unresponsive hosts Two hosts recovering the same stale socket could both unlink it, and a host shutting down could delete the socket that replaced its own. Recovery now runs under an exclusive lock file and checks the socket's identity before unlinking it. Shutdown removes only the socket the host bound, and a failed chmod shuts down cleanly. The extension now drops a host that sends nothing within 15 seconds or stops answering the heartbeat, so the reconnect alarm can start a new one instead of waiting forever. The listing and privacy policy now disclose personally identifiable information. Private fill handles user names and email addresses, and the agent can type personal details into forms. --- docs/DEVELOPER.md | 4 +- docs/PRIVACY.md | 6 +- site/privacy/index.html | 6 +- site/support/index.html | 10 ++- skills/chrome-control/SKILL.md | 3 - skills/chrome-control/native-host/host.js | 67 ++++++++++++++++-- src/extension/background.ts | 35 +++++++++- src/extension/images/icon.svg | 2 +- src/native-host/host.ts | 77 +++++++++++++++++++-- store/listing.md | 13 ++-- tests/security/host.test.ts | 70 +++++++++++++++++++ tests/security/native-host-controls.test.ts | 67 ++++++++++++++++-- tests/support/background.ts | 19 +++-- 13 files changed, 331 insertions(+), 48 deletions(-) diff --git a/docs/DEVELOPER.md b/docs/DEVELOPER.md index 68f018c..471e934 100644 --- a/docs/DEVELOPER.md +++ b/docs/DEVELOPER.md @@ -34,7 +34,9 @@ The popup has three controls: - **Pause host** disconnects the host, stops the reconnect and heartbeat alarms, and ends every session. The paused state is stored and survives service worker and browser restarts. - **Resume host** clears the paused state and reconnects. -When the host exits, the extension shows **Disconnected** and its reconnect alarm reconnects within 30 seconds. +The popup shows **Connecting** until the host sends its first message, because `connectNative` returns a port even when no host is installed. A host that does not answer within 15 seconds, or that fails the 30-second heartbeat, is dropped. When the host exits or is dropped, the extension shows **Disconnected** and its reconnect alarm reconnects within 30 seconds. + +The host removes a stale socket left by a crashed host only when a connection to it is refused. It does this under the exclusive lock file `.lock`, so two hosts cannot both replace the endpoint. At shutdown it removes the socket only if the socket is still the one it bound. ## Verify diff --git a/docs/PRIVACY.md b/docs/PRIVACY.md index 6c708c7..33510de 100644 --- a/docs/PRIVACY.md +++ b/docs/PRIVACY.md @@ -32,10 +32,10 @@ The extension handles data only when your local agent asks for it. "Agent tabs" | Screenshots of agent tabs (JPEG images) | The agent asks for a screenshot or a short recording. | So that the agent can see the page. | | Clicks and typed text | The agent clicks a control or types into a field that it found on the page (up to 2,000 characters for each field). | To carry out the task. | | Chrome DevTools Protocol results and events for agent tabs | The agent attaches the debugger to one of its tabs and sends DevTools Protocol commands. | For advanced automation. Depending on the commands and the events that the agent turns on, this can include the page structure, the results of scripts that the agent runs in the page, the addresses of pages and resources that the tab loads, other network activity and page events, and the page's cookies. | -| Sign-in values, such as a user name, password or one-time code | A local program on your computer asks the host to fill sign-in fields on a verified HTTPS page. | To sign in without putting the values in page readings. See [Private credential fill](#private-credential-fill). | +| Sign-in values, such as a user name or email address, a password or a one-time code | A local program on your computer asks the host to fill sign-in fields on a verified HTTPS page. | To sign in without putting the values in page readings. See [Private credential fill](#private-credential-fill). | | Path, name and size of a local PDF file | The agent attaches a local PDF to an upload field. | To attach the file. The extension gives the file path to Chrome. It does not read the contents of the file. | -Pages that you ask the agent to work on can incidentally contain other personal information, such as names, email addresses, messages, financial or health details, or your location. Browser Control does not look for, extract or keep these kinds of data separately. It handles them only as part of the website content described above: page text, screenshots and DevTools Protocol results from agent tabs. +Browser Control handles personally identifiable information in two ways: the user name or email address that a local program fills through private credential fill, and the names, addresses and other details that your agent types into forms for you. Pages that you ask the agent to work on can also contain other personal information, such as messages, financial or health details, or your location. Browser Control does not look for, extract or keep these kinds of data separately. It handles them only as part of the website content described above: page text, screenshots and DevTools Protocol results from agent tabs. ## How the data is used @@ -52,7 +52,7 @@ The extension keeps a small amount of state in `chrome.storage.local`: | `NATIVE_HOST_STATUS` | Connection state, host name, time of the last check, reconnect count and the last connection error message. | | `NATIVE_HOST_PAUSED` | Whether you paused the host. | | `TAB_GROUPS` | Agent session IDs, tab group IDs, tab IDs and group titles. | -| `extensionInstanceId` | A random ID created on first use. It is shared only with the local host. | +| `extensionInstanceId` | A random ID created on first use. It is shared only with the local host and with local programs that ask the host for the extension's details. | | `PRIVATE_CAPTURE_QUARANTINE:` | After a private fill: the page's document ID and the CSS selectors of the filled fields. It never contains the filled values. | In `chrome.storage.session`, which Chrome clears when the browser closes, the key `opChromePendingUpdateVersion` holds the version number of an extension update that waits until agent work ends. diff --git a/site/privacy/index.html b/site/privacy/index.html index aed7dfd..5cfac27 100644 --- a/site/privacy/index.html +++ b/site/privacy/index.html @@ -83,7 +83,7 @@

Data the extension handles

For advanced automation. Depending on the commands and the events that the agent turns on, this can include the page structure, the results of scripts that the agent runs in the page, the addresses of pages and resources that the tab loads, other network activity and page events, and the page’s cookies. - Sign-in values, such as a user name, password or one-time code + Sign-in values, such as a user name or email address, a password or a one-time code A local program on your computer asks the host to fill sign-in fields on a verified HTTPS page. To sign in without putting the values in page readings. See Private credential fill. @@ -95,7 +95,7 @@

Data the extension handles

-

Pages that you ask the agent to work on can incidentally contain other personal information, such as names, email addresses, messages, financial or health details, or your location. Browser Control does not look for, extract or keep these kinds of data separately. It handles them only as part of the website content described above: page text, screenshots and DevTools Protocol results from agent tabs.

+

Browser Control handles personally identifiable information in two ways: the user name or email address that a local program fills through private credential fill, and the names, addresses and other details that your agent types into forms for you. Pages that you ask the agent to work on can also contain other personal information, such as messages, financial or health details, or your location. Browser Control does not look for, extract or keep these kinds of data separately. It handles them only as part of the website content described above: page text, screenshots and DevTools Protocol results from agent tabs.

How the data is used

The extension uses data only to carry out the requests of the local agent that you connect, and to show the connection status in its popup. Data is not used for advertising, for profiles, to decide creditworthiness or for lending, or for any purpose unrelated to that single purpose.

@@ -113,7 +113,7 @@

In the extension’s storage

NATIVE_HOST_STATUSConnection state, host name, time of the last check, reconnect count and the last connection error message. NATIVE_HOST_PAUSEDWhether you paused the host. TAB_GROUPSAgent session IDs, tab group IDs, tab IDs and group titles. - extensionInstanceIdA random ID created on first use. It is shared only with the local host. + extensionInstanceIdA random ID created on first use. It is shared only with the local host and with local programs that ask the host for the extension's details. PRIVATE_CAPTURE_QUARANTINE:tab IDAfter a private fill: the page’s document ID and the CSS selectors of the filled fields. It never contains the filled values. diff --git a/site/support/index.html b/site/support/index.html index 8a803d9..f6faee0 100644 --- a/site/support/index.html +++ b/site/support/index.html @@ -66,18 +66,16 @@

The popup shows Disconnected

  • Check Node.js. The host needs Node.js 18 or later. The host launcher looks for node on the PATH, then in /opt/homebrew/bin and /usr/local/bin, and then uses the Node.js that ran the installer. If you use a Node.js version manager, run the installer with the version that you want Chrome to use.
  • Keep the helper folder in place. The manifest points to the host by its full path. If you moved or renamed the folder, run the installer again.
  • Reload. Open the popup and click Reload host. While the host is not paused, the extension also retries the connection every 30 seconds.
  • -
  • Remove a stale socket. The host refuses to start if the file ~/.opzero-chrome/default.sock already exists, or if the ~/.opzero-chrome folder is not private to your user account. This can happen after a crash. Quit Chrome, make sure that no host is still running, and then fix the folder: -
    pgrep -fl native-host/host.js
    -rm ~/.opzero-chrome/default.sock
    -chmod 700 ~/.opzero-chrome
    - If pgrep lists a process, quit it before you delete the file. Then start Chrome again.
  • +
  • Check the socket folder. After a crash, the host removes a leftover ~/.opzero-chrome/default.sock by itself when no running host answers on it. It still refuses to start if that path is not a socket, belongs to another user, or if the ~/.opzero-chrome folder is not private to your user account. To make the folder private: +
    chmod 700 ~/.opzero-chrome
    + Then click Reload host.
  • Use one Chrome profile at a time. All Chrome profiles share the same host manifest and socket. If Browser Control is turned on in more than one profile, only the first profile to start the host connects. Pause the host in the other profiles.
  • Wait for the version check. If node native-host/client.js ping reports Extension protocol v2 is not ready, the host is still checking the extension. This takes up to 10 seconds after the host starts. If the message stays, update the extension and the helper to matching versions, and then click Reload host.
  • Other browsers. The installer registers the host for Google Chrome™ only. For another browser, pass --manifest-path with the full path of the com.opzero.chrome.json file in that browser’s native messaging hosts folder.
  • -

    Open the Browser Control popup from the Chrome toolbar. The status shows Connected, Paused or Disconnected. When the host is not connected, or when there is an error, the popup also shows the host name, the time of the last check and the last error.

    +

    Open the Browser Control popup from the Chrome toolbar. The status shows Connected, Connecting, Paused or Disconnected. Connecting means that Chrome started the host and the extension is waiting for its first reply. If the host does not reply within 15 seconds, or later stops answering, the extension drops it and starts a new one. When the host is not connected, or when there is an error, the popup also shows the host name, the time of the last check and the last error.

    Reload host
    Turns off pause, closes the connection and opens it again, so Chrome starts a new host process. Use it after you install or update the helper. If the popup still shows Disconnected, wait 30 seconds or click the button again.
    diff --git a/skills/chrome-control/SKILL.md b/skills/chrome-control/SKILL.md index 2899c19..d9516f1 100644 --- a/skills/chrome-control/SKILL.md +++ b/skills/chrome-control/SKILL.md @@ -96,7 +96,6 @@ Use: ```sh node native-host/client.js getInfo node native-host/client.js getUserTabs -node native-host/client.js getUserHistory '{"limit":10}' ``` Session-scoped calls require both `session_id` and `turn_id`: @@ -198,7 +197,6 @@ The background service worker exposes: - `getInfo` - `getTabs` - `getUserTabs` -- `getUserHistory` - `createTab` - `claimUserTab` - `finalizeTabs` @@ -214,5 +212,4 @@ The extension forwards these notifications when active: - `onCDPEvent` - `onCDPDetach` -- `onDownloadChange` - `onControlStopped` diff --git a/skills/chrome-control/native-host/host.js b/skills/chrome-control/native-host/host.js index 6504d3f..03f2aaa 100644 --- a/skills/chrome-control/native-host/host.js +++ b/skills/chrome-control/native-host/host.js @@ -29,6 +29,10 @@ var clients = /* @__PURE__ */ new Map(); var pending = /* @__PURE__ */ new Map(); var nextId = 1; var ownsSocket = false; +var boundSocket; +var holdsRecoveryLock = false; +var recoveryLockPath = `${socketPath}.lock`; +var staleRecoveryLockMs = 1e4; var tcpToken; function native(message) { const body = Buffer.from(JSON.stringify(message)); @@ -247,9 +251,8 @@ var server = node_net.default.createServer((socket) => { }); function shutdown(code) { for (const socket of clients.keys()) socket.destroy(); - if (ownsSocket) try { - node_fs.default.unlinkSync(socketPath); - } catch {} + if (ownsSocket) removeOwnSocket(); + releaseRecoveryLock(); node_process.default.exit(code); } server.on("error", () => { @@ -290,11 +293,63 @@ function refuseEndpoint() { } function listenUnix() { server.listen(socketPath, () => { - ownsSocket = true; - node_fs.default.chmodSync(socketPath, 384); + try { + const bound = node_fs.default.lstatSync(socketPath); + boundSocket = { + dev: bound.dev, + ino: bound.ino + }; + ownsSocket = true; + node_fs.default.chmodSync(socketPath, 384); + releaseRecoveryLock(); + } catch { + refuseEndpoint(); + } }); } +function removeOwnSocket() { + try { + const current = node_fs.default.lstatSync(socketPath); + if (boundSocket && current.dev === boundSocket.dev && current.ino === boundSocket.ino) node_fs.default.unlinkSync(socketPath); + } catch {} +} +function acquireRecoveryLock(retry = true) { + try { + node_fs.default.closeSync(node_fs.default.openSync(recoveryLockPath, "wx", 384)); + holdsRecoveryLock = true; + return true; + } catch (lockError) { + if (lockError.code !== "EEXIST" || !retry) return false; + try { + const lock = node_fs.default.lstatSync(recoveryLockPath); + if (!lock.isFile() || lock.uid !== node_process.default.getuid?.() || Date.now() - lock.mtimeMs < staleRecoveryLockMs) return false; + node_fs.default.unlinkSync(recoveryLockPath); + } catch { + return false; + } + return acquireRecoveryLock(false); + } +} +function releaseRecoveryLock() { + if (!holdsRecoveryLock) return; + holdsRecoveryLock = false; + try { + node_fs.default.unlinkSync(recoveryLockPath); + } catch {} +} function reclaimStaleSocket() { + if (!acquireRecoveryLock()) { + refuseEndpoint(); + return; + } + let stale; + try { + stale = node_fs.default.lstatSync(socketPath); + if (!stale.isSocket() || stale.uid !== node_process.default.getuid?.()) throw new Error("endpoint exists"); + } catch { + refuseEndpoint(); + return; + } const probe = node_net.default.connect(socketPath); probe.once("connect", () => { probe.destroy(); @@ -306,6 +361,8 @@ function reclaimStaleSocket() { return; } try { + const current = node_fs.default.lstatSync(socketPath); + if (current.dev !== stale.dev || current.ino !== stale.ino) throw new Error("endpoint changed"); node_fs.default.unlinkSync(socketPath); } catch { refuseEndpoint(); diff --git a/src/extension/background.ts b/src/extension/background.ts index ad63fef..2f5a196 100644 --- a/src/extension/background.ts +++ b/src/extension/background.ts @@ -25,6 +25,7 @@ const DEBUGGER_VERSION = "1.3"; const DEFAULT_CDP_TIMEOUT_MS = 10000; const MESSAGE_TIMEOUT_MS = 1000; const HEARTBEAT_TIMEOUT_MS = 3000; +const HOST_HANDSHAKE_TIMEOUT_MS = 15000; type RpcId = number | string | null | undefined; type JsonRecord = Record; @@ -379,6 +380,7 @@ class NativeTransport { reconnectAttempt = 0; connected = false; paused = false; + handshakeTimer: ReturnType | undefined; constructor(hostName: string) { this.hostName = hostName; @@ -418,6 +420,7 @@ class NativeTransport { } disconnect(message = "Native host disconnected") { + this.clearHandshake(); const port = this.port; this.port = null; this.connected = false; @@ -485,6 +488,10 @@ class NativeTransport { this.connected = false; this.setStatus("connecting").catch(() => undefined); const port = this.port; + this.clearHandshake(); + this.handshakeTimer = setTimeout(() => { + if (this.port === port && !this.connected) this.failPort("Native host did not respond"); + }, HOST_HANDSHAKE_TIMEOUT_MS); port.onMessage.addListener((message) => { if (this.port !== port) return; this.markConnected(); @@ -506,13 +513,29 @@ class NativeTransport { // message from the host proves that it is running. markConnected() { if (this.connected) return; + this.clearHandshake(); this.connected = true; this.reconnectAttempt = 0; this.setStatus("connected").catch(() => undefined); } + clearHandshake() { + if (this.handshakeTimer !== undefined) clearTimeout(this.handshakeTimer); + this.handshakeTimer = undefined; + } + + // Drops a port whose host never answered or stopped answering, so the + // reconnect alarm can start a fresh host instead of waiting forever. + failPort(message: string) { + this.disconnect(message); + if (this.paused) return; + this.reconnectAttempt += 1; + this.setStatus("disconnected", { error: message, nextRetryMs: 5000 }).catch(() => undefined); + } + onDisconnect() { const message = chrome.runtime.lastError?.message || "Native host disconnected"; + this.clearHandshake(); this.connected = false; this.port = null; revokeAllSessions(); @@ -776,7 +799,7 @@ const api: Record = { getInfo: () => Effect.gen(function* () { return { - name: "Chrome", + name: chrome.runtime.getManifest().name, version: chrome.runtime.getManifest().version, protocolVersion: 2, pageProtocolVersion: 2, @@ -1399,8 +1422,14 @@ function revokeAllSessions() { chrome.alarms.onAlarm.addListener((alarm) => { if (alarm.name === RECONNECT_ALARM && !nativeTransport?.connected && !nativeTransport?.paused) nativeTransport.connect(); if (alarm.name === HEARTBEAT_ALARM && nativeTransport?.connected) { - withTimeout(nativeTransport.call("ping", {}, HEARTBEAT_TIMEOUT_MS), HEARTBEAT_TIMEOUT_MS, "Native heartbeat") - .catch((error) => runChromeEffect(stopActiveSessions(error instanceof Error ? error.message : String(error))).catch(() => undefined)); + const transport = nativeTransport; + const port = transport.port; + withTimeout(transport.call("ping", {}, HEARTBEAT_TIMEOUT_MS), HEARTBEAT_TIMEOUT_MS, "Native heartbeat") + .catch(async (error) => { + const message = error instanceof Error ? error.message : String(error); + await runChromeEffect(stopActiveSessions(message)).catch(() => undefined); + if (transport.port === port) transport.failPort(message); + }); } }); diff --git a/src/extension/images/icon.svg b/src/extension/images/icon.svg index 516f503..04bc6a2 100644 --- a/src/extension/images/icon.svg +++ b/src/extension/images/icon.svg @@ -1,4 +1,4 @@ - + diff --git a/src/native-host/host.ts b/src/native-host/host.ts index 3f79820..70f0231 100755 --- a/src/native-host/host.ts +++ b/src/native-host/host.ts @@ -21,6 +21,10 @@ const clients = new Map(); let nextId = 1; let ownsSocket = false; +let boundSocket: { dev: number; ino: number } | undefined; +let holdsRecoveryLock = false; +const recoveryLockPath = `${socketPath}.lock`; +const staleRecoveryLockMs = 10000; let tcpToken: Buffer | undefined; function native(message: unknown) { @@ -186,7 +190,8 @@ const server = net.createServer(socket => { function shutdown(code: number) { for (const socket of clients.keys()) socket.destroy(); - if (ownsSocket) { try { fs.unlinkSync(socketPath); } catch {} } + if (ownsSocket) removeOwnSocket(); + releaseRecoveryLock(); process.exit(code); } @@ -226,18 +231,80 @@ function refuseEndpoint() { shutdown(1); } +// A bind never replaces an existing file, so a racing host fails with +// EADDRINUSE instead of taking over this endpoint. function listenUnix() { - server.listen(socketPath, () => { ownsSocket = true; fs.chmodSync(socketPath, 0o600); }); + server.listen(socketPath, () => { + try { + const bound = fs.lstatSync(socketPath); + boundSocket = { dev: bound.dev, ino: bound.ino }; + ownsSocket = true; + fs.chmodSync(socketPath, 0o600); + releaseRecoveryLock(); + } catch { + refuseEndpoint(); + } + }); +} + +// Another host may have replaced this endpoint after a stale-socket recovery, +// so only the socket this host bound is removed. +function removeOwnSocket() { + try { + const current = fs.lstatSync(socketPath); + if (boundSocket && current.dev === boundSocket.dev && current.ino === boundSocket.ino) fs.unlinkSync(socketPath); + } catch {} +} + +function acquireRecoveryLock(retry = true): boolean { + try { + fs.closeSync(fs.openSync(recoveryLockPath, "wx", 0o600)); + holdsRecoveryLock = true; + return true; + } catch (lockError) { + if ((lockError as NodeJS.ErrnoException).code !== "EEXIST" || !retry) return false; + try { + const lock = fs.lstatSync(recoveryLockPath); + if (!lock.isFile() || lock.uid !== process.getuid?.() || Date.now() - lock.mtimeMs < staleRecoveryLockMs) return false; + fs.unlinkSync(recoveryLockPath); + } catch { + return false; + } + return acquireRecoveryLock(false); + } +} + +function releaseRecoveryLock() { + if (!holdsRecoveryLock) return; + holdsRecoveryLock = false; + try { fs.unlinkSync(recoveryLockPath); } catch {} } -// A host killed without cleanup leaves its socket file behind. Only a -// refused connection proves no live host owns it; anything else is left alone. +// A host killed without cleanup leaves its socket file behind. Recovery runs +// under an exclusive lock so two hosts cannot both unlink the endpoint, and +// only a refused connection proves that no live host owns the socket. function reclaimStaleSocket() { + if (!acquireRecoveryLock()) { refuseEndpoint(); return; } + let stale: fs.Stats; + try { + stale = fs.lstatSync(socketPath); + if (!stale.isSocket() || stale.uid !== process.getuid?.()) throw new Error("endpoint exists"); + } catch { + refuseEndpoint(); + return; + } const probe = net.connect(socketPath); probe.once("connect", () => { probe.destroy(); refuseEndpoint(); }); probe.once("error", (probeError: NodeJS.ErrnoException) => { if (probeError.code !== "ECONNREFUSED") { refuseEndpoint(); return; } - try { fs.unlinkSync(socketPath); } catch { refuseEndpoint(); return; } + try { + const current = fs.lstatSync(socketPath); + if (current.dev !== stale.dev || current.ino !== stale.ino) throw new Error("endpoint changed"); + fs.unlinkSync(socketPath); + } catch { + refuseEndpoint(); + return; + } listenUnix(); }); } diff --git a/store/listing.md b/store/listing.md index 19d1feb..0c47f28 100644 --- a/store/listing.md +++ b/store/listing.md @@ -176,10 +176,10 @@ Keeps the agent's work visible and separate from the user's own tabs. Every tab #### `tabs` -692 / 1,000 characters. +765 / 1,000 characters. ```text -Lets the user's local agent see and use tabs. getUserTabs calls chrome.tabs.query and returns the ID, URL, title, window, position and group of the user's open tabs, leaving out Chrome internal pages, so the agent can pick one to claim. createTab opens an inactive blank tab in a normal window, and claimUserTab adds a tab the agent picked to its session. The extension also groups and ungroups agent tabs, messages its cursor overlay (chrome.tabs.sendMessage), and, when the agent finishes (finalizeTabs), closes only tabs the agent opened. It listens to chrome.tabs.onUpdated, onRemoved and onReplaced to cancel stale actions after a navigation or when a tab closes. Tab data is not stored. +Lets the user's local agent see and use tabs. getUserTabs calls chrome.tabs.query and returns the ID, URL, title, window, position and group of the user's open tabs, leaving out Chrome internal pages, so the agent can pick one to claim. createTab opens an inactive blank tab in a normal window, and claimUserTab adds a tab the agent picked to its session. The extension also groups and ungroups agent tabs, messages its cursor overlay (chrome.tabs.sendMessage), and, when the agent finishes (finalizeTabs), closes only tabs the agent opened. It listens to chrome.tabs.onUpdated, onRemoved and onReplaced to cancel stale actions after a navigation or when a tab closes. Tab URLs and titles are not stored; only tab, group and session IDs are kept for the tab groups. ``` ### Host permission justification @@ -194,10 +194,10 @@ The user decides which websites their local agent works on, so the sites cannot Answer: **No, I am not using remote code.** -Justification, 856 / 1,000 characters: +Justification, 865 / 1,000 characters: ```text -No. Every script the extension runs is in the package: background.js, popup.js, content-scripts/opzero-chrome.js, and page functions bundled in background.js and injected with chrome.scripting.executeScript({ func }). The extension loads no script from any URL, has no script tag that points outside the package, and never passes strings to eval() or new Function(). Its extension pages use the CSP script-src 'self'. The minified Effect library in the bundles has object methods named eval; they are ordinary methods, not the global eval. For completeness: the executeCdp request can relay a DevTools Protocol command, such as Runtime.evaluate, from the user's own local agent to a page in that agent's tab. That input comes from software on the user's device over native messaging, not from a remote server, and it runs in the page, not in the extension. +No. All extension code is in the package: background.js, popup.js, content-scripts/opzero-chrome.js, and page functions bundled in background.js and injected with chrome.scripting.executeScript({ func }). The extension loads no script from any URL, has no script tag that points outside the package, and never passes strings to eval() or new Function(). Its extension pages use the CSP script-src 'self'. The minified Effect library in the bundles has object methods named eval; they are ordinary methods, not the global eval. Separately, through the chrome.debugger API, the executeCdp request relays DevTools Protocol commands, such as Runtime.evaluate, from the user's own local agent to a page in that agent's tab. Those commands come from software on the user's device over native messaging, not from a server, and they run in the page, never in the extension. ``` ### Data usage @@ -206,7 +206,7 @@ Google: "Extensions are required to disclose how they handle user data, even whe | Category | Mark | Reason (from the audit) | | --- | --- | --- | -| Personally identifiable information | Unchecked | No code collects or extracts names, addresses, email addresses or ID numbers. Such details can appear by chance on a page that the agent reads. They are then handled only as Website content, and the privacy policy says so. | +| Personally identifiable information | **Checked** | Private fill passes a user name or email address from a local program into a verified sign-in field, without storing or logging it (`private-input.ts` accepts text and email fields). The agent can also type names, addresses and similar details into form fields (`actPage`). Details that only appear on a page are handled as Website content. | | Health information | Unchecked | No code reads health data. Health details that appear on a page are handled only as Website content. | | Financial and payment information | Unchecked | No code reads payment, card or transaction data. Financial details that appear on a page are handled only as Website content. | | Authentication information | **Checked** | Private fill passes passwords and one-time codes from a local program into verified sign-in fields, without storing or logging them (`privateFill`, `background.ts:972-998`; `private-input.ts:25-56`). The `executeCdp` relay (`background.ts:906-926`) can return the cookies of agent tabs. | @@ -238,7 +238,8 @@ They are true because the extension sends data only to the host on the user's de | Web history (checked) | Table row "Tab details". Page addresses: table row "Chrome DevTools Protocol results and events". | | User activity (checked) | Table row "Chrome DevTools Protocol results and events" ("other network activity and page events") | | Website content (checked) | Table rows "Page content", "Screenshots" and "Chrome DevTools Protocol results and events", and the paragraph after the table | -| Personally identifiable, health, financial, personal communications and location (unchecked) | The paragraph after the table: such details are handled "only as part of the website content described above" | +| Personally identifiable information (checked) | Table rows "Sign-in values" and "Clicks and typed text", and the paragraph after the table | +| Health, financial, personal communications and location (unchecked) | The paragraph after the table: such details are handled "only as part of the website content described above" | | Storage justification keys | "In the extension's storage": the same five `chrome.storage.local` keys and one `chrome.storage.session` key | | Remote code: No | "What we do not do", the remote code bullet | | Certifications | "What we do not do", and the Limited Use sentence | diff --git a/tests/security/host.test.ts b/tests/security/host.test.ts index b661d2d..281f91f 100644 --- a/tests/security/host.test.ts +++ b/tests/security/host.test.ts @@ -181,6 +181,76 @@ it("reclaims the stale endpoint of a host that was killed without cleanup", asyn expect(fs.statSync(h.endpoint).mode & 0o777).toBe(0o600); }); +function spawnHost(endpoint: string) { + const child = spawn(process.execPath, ["dist/native-host/host.js"], { + env: { ...process.env, OPZERO_CHROME_HOST_SOCKET: endpoint }, stdio: ["pipe", "ignore", "pipe"] + }); + cleanup.push(() => child.kill()); + const exited = new Promise(resolve => child.once("exit", resolve)); + return { child, exited }; +} + +function hostInfo(endpoint: string) { + return new Promise((resolve, reject) => { + const socket = net.connect(endpoint); + socket.setEncoding("utf8"); + socket.once("error", reject); + socket.once("connect", () => socket.write(`${JSON.stringify({ jsonrpc: "2.0", id: 1, method: "host.info" })}\n`)); + socket.once("data", chunk => { socket.destroy(); resolve(JSON.parse(String(chunk)).result); }); + }); +} + +async function staleEndpoint() { + const h = await host(); + const exited = new Promise(resolve => h.child.once("exit", resolve)); + h.child.kill("SIGKILL"); + await exited; + return h.endpoint; +} + +it("leaves exactly one live host when several recover the same stale endpoint at once", async () => { + for (let round = 0; round < 5; round++) { + const endpoint = await staleEndpoint(); + const hosts = Array.from({ length: 4 }, () => spawnHost(endpoint)); + await vi.waitFor(async () => expect((await hostInfo(endpoint)).epoch).toBeTypeOf("string")); + await new Promise(resolve => setTimeout(resolve, 300)); + const alive = hosts.filter(h => h.child.exitCode === null); + expect(alive).toHaveLength(1); + expect((await hostInfo(endpoint)).epoch).toBeTypeOf("string"); + expect(fs.existsSync(`${endpoint}.lock`)).toBe(false); + alive[0].child.kill("SIGTERM"); + await alive[0].exited; + expect(fs.existsSync(endpoint)).toBe(false); + } +}); + +it("fails closed while another host holds a fresh recovery lock and clears a stale one", async () => { + const endpoint = await staleEndpoint(); + const lock = `${endpoint}.lock`; + fs.writeFileSync(lock, ""); + expect(await spawnHost(endpoint).exited).toBe(1); + expect(fs.lstatSync(endpoint).isSocket()).toBe(true); + expect(fs.existsSync(lock)).toBe(true); + const past = new Date(Date.now() - 60000); + fs.utimesSync(lock, past, past); + const recovered = spawnHost(endpoint); + await vi.waitFor(async () => expect((await hostInfo(endpoint)).epoch).toBeTypeOf("string")); + expect(recovered.child.exitCode).toBeNull(); + expect(fs.existsSync(lock)).toBe(false); +}); + +it("does not remove a socket that replaced its own before it shuts down", async () => { + const h = await host(); + const exited = new Promise(resolve => h.child.once("exit", resolve)); + fs.unlinkSync(h.endpoint); + const replacement = net.createServer(socket => socket.end()); + cleanup.push(() => replacement.close()); + await new Promise((resolve, reject) => { replacement.once("error", reject); replacement.listen(h.endpoint, resolve); }); + h.child.kill("SIGTERM"); + await exited; + expect(fs.lstatSync(h.endpoint).isSocket()).toBe(true); +}); + it("refuses an endpoint path that holds something other than a socket", async () => { const directory = testTemp(); cleanup.push(() => fs.rmSync(directory, { recursive: true, force: true })); diff --git a/tests/security/native-host-controls.test.ts b/tests/security/native-host-controls.test.ts index f0e62bb..bef805d 100644 --- a/tests/security/native-host-controls.test.ts +++ b/tests/security/native-host-controls.test.ts @@ -2,10 +2,23 @@ import { expect, it, vi } from "vitest"; import { background } from "../support/background"; const reconnectAlarm = { name: "native-transport-reconnect:com.opzero.chrome" }; +const heartbeatAlarm = { name: "client-heartbeat-alarm" }; const hostProbe = { jsonrpc: "2.0", id: "protocol:test", method: "getInfo", params: {} }; +const handshakeTimeoutMs = 15000; -function hostState(b: Awaited>) { - return (b.storage.NATIVE_HOST_STATUS as { state: string; error?: string } | undefined); +type Harness = Awaited>; + +function hostState(b: Harness) { + return b.storage.NATIVE_HOST_STATUS as { state: string; error?: string } | undefined; +} + +function capturingHandshake() { + const handshakes: (() => void)[] = []; + const timer = ((callback: () => void, ms?: number, ...args: unknown[]) => { + if (ms === handshakeTimeoutMs) { handshakes.push(callback); return 0; } + return setTimeout(callback, ms, ...args); + }) as typeof setTimeout; + return { handshakes, timer }; } it("keeps the native host paused across reconnect alarms and worker restarts until resumed", async () => { @@ -14,7 +27,7 @@ it("keeps the native host paused across reconnect alarms and worker restarts unt const paused = await b.popup("PAUSE_NATIVE_HOST"); expect(paused.status.state).toBe("paused"); - expect(b.port.disconnect).toHaveBeenCalledTimes(1); + expect(b.ports[0].disconnect).toHaveBeenCalledTimes(1); expect(b.storage.NATIVE_HOST_PAUSED).toBe(true); expect(b.chrome.alarms.clear).toHaveBeenCalledWith(reconnectAlarm.name); @@ -42,16 +55,20 @@ it("reloads the native host on a fresh port and reconnects after the host exits" const reloaded = await b.popup("RELOAD_NATIVE_HOST"); expect(reloaded.status.state).toBe("connecting"); - expect(b.port.disconnect).toHaveBeenCalledTimes(1); - expect(b.chrome.runtime.connectNative).toHaveBeenCalledTimes(2); + expect(b.ports[0].disconnect).toHaveBeenCalledTimes(1); + expect(b.ports).toHaveLength(2); expect(b.storage.NATIVE_HOST_PAUSED).toBe(false); expect((await b.rpc("bindPage", owner)).error).toBeDefined(); await vi.waitFor(() => expect(hostState(b)?.state).toBe("connected")); + b.ports[0].onMessage.emit(hostProbe); + b.ports[0].onDisconnect.emit(); + expect(hostState(b)?.state).toBe("connected"); + b.port.onDisconnect.emit(); await vi.waitFor(() => expect(hostState(b)?.state).toBe("disconnected")); b.chrome.alarms.onAlarm.emit(reconnectAlarm); - expect(b.chrome.runtime.connectNative).toHaveBeenCalledTimes(3); + expect(b.ports).toHaveLength(3); await vi.waitFor(() => expect(hostState(b)?.state).toBe("connecting")); b.port.onMessage.emit(hostProbe); await vi.waitFor(() => expect(hostState(b)?.state).toBe("connected")); @@ -72,3 +89,41 @@ it("never reports a missing native host as connected", async () => { await vi.waitFor(() => expect(hostState(b)).toMatchObject({ state: "disconnected", error: "Specified native messaging host not found." })); expect(states).not.toContain("connected"); }); + +it("drops a host that never answers and reconnects on the next alarm", async () => { + const { handshakes, timer } = capturingHandshake(); + const b = await background({}, { setTimeout: timer }); + expect(handshakes).toHaveLength(1); + expect(hostState(b)?.state).toBe("connecting"); + + handshakes[0](); + await vi.waitFor(() => expect(hostState(b)).toMatchObject({ state: "disconnected", error: "Native host did not respond" })); + expect(b.ports[0].disconnect).toHaveBeenCalledTimes(1); + + b.chrome.alarms.onAlarm.emit(reconnectAlarm); + expect(b.ports).toHaveLength(2); + b.port.onMessage.emit(hostProbe); + await vi.waitFor(() => expect(hostState(b)?.state).toBe("connected")); + handshakes[1](); + expect(hostState(b)?.state).toBe("connected"); + expect(b.ports[1].disconnect).not.toHaveBeenCalled(); +}); + +it("replaces a host that stops answering the heartbeat", async () => { + const b = await background(); + b.port.onMessage.emit(hostProbe); + await vi.waitFor(() => expect(hostState(b)?.state).toBe("connected")); + + b.chrome.alarms.onAlarm.emit(heartbeatAlarm); + const ping = await vi.waitFor(() => { + const request = b.responses.find(message => message.method === "ping"); + expect(request).toBeDefined(); + return request; + }); + b.port.onMessage.emit({ jsonrpc: "2.0", id: ping.id, error: { code: -32000, message: "Native heartbeat timed out" } }); + await vi.waitFor(() => expect(hostState(b)?.state).toBe("disconnected")); + expect(b.ports[0].disconnect).toHaveBeenCalledTimes(1); + + b.chrome.alarms.onAlarm.emit(reconnectAlarm); + expect(b.ports).toHaveLength(2); +}); diff --git a/tests/support/background.ts b/tests/support/background.ts index a1bac05..f187867 100644 --- a/tests/support/background.ts +++ b/tests/support/background.ts @@ -8,15 +8,22 @@ function event() { return { addListener: (fn: (...args: any[]) => void) => listeners.push(fn), emit: (...args: any[]) => listeners.forEach(fn => fn(...args)) }; } -export async function background(initialStorage: Record = {}) { +type Timers = { setTimeout?: typeof setTimeout }; + +export async function background(initialStorage: Record = {}, timers: Timers = {}) { const responses: any[] = []; const tabs = new Map(); let nextTab = 1; - const port = { onMessage: event(), onDisconnect: event(), postMessage: (msg: any) => responses.push(msg), disconnect: vi.fn() }; + const ports: any[] = []; + const newPort = () => { + const port = { onMessage: event(), onDisconnect: event(), postMessage: (msg: any) => responses.push(msg), disconnect: vi.fn() }; + ports.push(port); + return port; + }; const storage: Record = { ...initialStorage }; let group = { id: 1, title: "", collapsed: false }; const chrome = { - runtime: { connectNative: vi.fn(() => port), getManifest: () => ({ version: "test" }), id: "test", onMessage: event(), onStartup: event(), onInstalled: event(), onUpdateAvailable: event(), reload: vi.fn() }, + runtime: { connectNative: vi.fn(() => newPort()), getManifest: () => ({ name: "Browser Control", version: "test" }), id: "test", onMessage: event(), onStartup: event(), onInstalled: event(), onUpdateAvailable: event(), reload: vi.fn() }, storage: { local: { get: (key: string, cb: Function) => cb({ [key]: storage[key] }), set: (value: object, cb: Function) => { Object.assign(storage, value); cb(); } }, session: { get: (_: unknown, cb: Function) => cb({}) } }, alarms: { create: vi.fn(), clear: vi.fn(), onAlarm: event() }, windows: { getCurrent: (_: unknown, cb: Function) => cb({ id: 1, type: "normal" }) }, @@ -34,17 +41,17 @@ export async function background(initialStorage: Record = {}) { debugger: { attach: vi.fn((_: unknown, __: unknown, cb: Function) => cb()), detach: (_: unknown, cb: Function) => cb(), getTargets: (cb: Function) => cb([{ tabId: 1 }, { tabId: 2 }]), sendCommand: vi.fn((_: unknown, __: unknown, ___: unknown, cb: Function) => cb({})), onEvent: event(), onDetach: event() }, scripting: { executeScript: vi.fn((_: unknown, cb: Function) => cb([{ documentId: "doc-1", frameId: 0, result: { status: "observed", origin: "https://synthetic.invalid", url: "https://synthetic.invalid/" } }])) } }; - vm.runInNewContext(fs.readFileSync("dist/extension/background.js", "utf8"), { chrome, crypto: webcrypto, setTimeout, clearTimeout, console, URL, TextEncoder, TextDecoder, AbortController }); + vm.runInNewContext(fs.readFileSync("dist/extension/background.js", "utf8"), { chrome, crypto: webcrypto, setTimeout: timers.setTimeout ?? setTimeout, clearTimeout, console, URL, TextEncoder, TextDecoder, AbortController }); await new Promise(resolve => setTimeout(resolve, 10)); let id = 0; async function rpc(method: string, params: Record = {}) { const requestId = ++id; - port.onMessage.emit({ jsonrpc: "2.0", id: requestId, method, params }); + ports[ports.length - 1].onMessage.emit({ jsonrpc: "2.0", id: requestId, method, params }); await vi.waitFor(() => expect(responses.find(r => r.id === requestId)).toBeDefined()); return responses.find(r => r.id === requestId); } function popup(type: string) { return new Promise(resolve => chrome.runtime.onMessage.emit({ type }, {}, resolve)); } - return { chrome, port, responses, rpc, popup, storage }; + return { chrome, get port() { return ports[ports.length - 1]; }, ports, responses, rpc, popup, storage }; } From 5717395c06a2c193b68acbce86c7998b9ebed000 Mon Sep 17 00:00:00 2001 From: afif Date: Mon, 28 Sep 2026 16:22:33 +0800 Subject: [PATCH 14/17] Hold one startup lock for every host start and guard stale heartbeats A fresh host could bind while a recovering host probed the same path, and the recovery lock's age rule could evict a live owner. Every Unix startup now holds one lock from its first look at the socket until the new socket listens. The lock appears atomically with its owner's pid and is stale only when that process is dead. A heartbeat that fails after a reload no longer stops the new connection's sessions. --- docs/DEVELOPER.md | 2 +- docs/PRIVACY.md | 2 +- site/privacy/index.html | 2 +- skills/chrome-control/native-host/host.js | 118 +++++++++++++------- src/extension/background.ts | 1 + src/native-host/host.ts | 111 ++++++++++++------ store/listing.md | 6 +- tests/security/host.test.ts | 42 +++++-- tests/security/native-host-controls.test.ts | 17 +++ tests/support/background.ts | 3 +- 10 files changed, 212 insertions(+), 92 deletions(-) diff --git a/docs/DEVELOPER.md b/docs/DEVELOPER.md index 471e934..7ad1bbc 100644 --- a/docs/DEVELOPER.md +++ b/docs/DEVELOPER.md @@ -36,7 +36,7 @@ The popup has three controls: The popup shows **Connecting** until the host sends its first message, because `connectNative` returns a port even when no host is installed. A host that does not answer within 15 seconds, or that fails the 30-second heartbeat, is dropped. When the host exits or is dropped, the extension shows **Disconnected** and its reconnect alarm reconnects within 30 seconds. -The host removes a stale socket left by a crashed host only when a connection to it is refused. It does this under the exclusive lock file `.lock`, so two hosts cannot both replace the endpoint. At shutdown it removes the socket only if the socket is still the one it bound. +Every Unix host startup holds the lock file `.lock`, from its first look at the socket path until its own socket is listening. The lock holds the owner's process ID. A new host treats it as stale only if that process is dead, or if the lock is older than five minutes, which only happens when a process ID has been reused. While the lock is held, the host removes a socket left by a crashed host only if a connection to it is refused. At shutdown it removes the socket only if it is still the one it bound. A host that cannot take the lock exits, and the extension retries within 30 seconds. ## Verify diff --git a/docs/PRIVACY.md b/docs/PRIVACY.md index 33510de..b5708a3 100644 --- a/docs/PRIVACY.md +++ b/docs/PRIVACY.md @@ -67,7 +67,7 @@ These entries stay in Chrome until you remove the extension. The extension does - Windows: `%USERPROFILE%\AppData\Local\opzero-chrome\`, plus the registry key `HKCU\Software\Google\Chrome\NativeMessagingHosts\com.opzero.chrome` It also writes a launcher script in the `native-host` folder and the file `scripts/extension-id.json` inside the helper folder that you unpacked. -- **Host.** The host creates the private folder `~/.opzero-chrome` and a socket file in it, which it deletes when it stops. The host keeps no log files. The only messages that it writes to its error output are fixed text with no user data. +- **Host.** The host creates the private folder `~/.opzero-chrome` and a socket file in it, which it deletes when it stops. While it starts, it also holds a small lock file next to the socket that contains only its process ID, and it deletes the lock as soon as the socket is ready. The host keeps no log files. The only messages that it writes to its error output are fixed text with no user data. - **Screenshots.** The host passes screenshots to the requesting client in memory. It does not save them. The agent decides whether to keep them. - **Recordings.** When your agent asks for a short recording, the client library in the helper folder (`native-host/transport.js`) saves JPEG frames, a `frames.ffconcat` list, a `capture.json` summary with the time and SHA-256 hash of each frame and, if `ffmpeg` is installed, a `recording.mp4` file. It puts them in a new `tab-video-` folder inside a private folder that your agent tooling chooses, with access restricted to your user account. A recording lasts at most 60 seconds (30 seconds by default) and stops at 100 MB. The files stay until you delete them. diff --git a/site/privacy/index.html b/site/privacy/index.html index 5cfac27..60a61ae 100644 --- a/site/privacy/index.html +++ b/site/privacy/index.html @@ -130,7 +130,7 @@

    Files on your computer

  • Windows: %USERPROFILE%\AppData\Local\opzero-chrome\, plus the registry key HKCU\Software\Google\Chrome\NativeMessagingHosts\com.opzero.chrome
  • It also writes a launcher script in the native-host folder and the file scripts/extension-id.json inside the helper folder that you unpacked. -
  • Host. The host creates the private folder ~/.opzero-chrome and a socket file in it, which it deletes when it stops. The host keeps no log files. The only messages that it writes to its error output are fixed text with no user data.
  • +
  • Host. The host creates the private folder ~/.opzero-chrome and a socket file in it, which it deletes when it stops. While it starts, it also holds a small lock file next to the socket that contains only its process ID, and it deletes the lock as soon as the socket is ready. The host keeps no log files. The only messages that it writes to its error output are fixed text with no user data.
  • Screenshots. The host passes screenshots to the requesting client in memory. It does not save them. The agent decides whether to keep them.
  • Recordings. When your agent asks for a short recording, the client library in the helper folder (native-host/transport.js) saves JPEG frames, a frames.ffconcat list, a capture.json summary with the time and SHA-256 hash of each frame and, if ffmpeg is installed, a recording.mp4 file. It puts them in a new tab-video- folder inside a private folder that your agent tooling chooses, with access restricted to your user account. A recording lasts at most 60 seconds (30 seconds by default) and stops at 100 MB. The files stay until you delete them.
  • diff --git a/skills/chrome-control/native-host/host.js b/skills/chrome-control/native-host/host.js index 03f2aaa..6ad69ab 100644 --- a/skills/chrome-control/native-host/host.js +++ b/skills/chrome-control/native-host/host.js @@ -30,9 +30,9 @@ var pending = /* @__PURE__ */ new Map(); var nextId = 1; var ownsSocket = false; var boundSocket; -var holdsRecoveryLock = false; -var recoveryLockPath = `${socketPath}.lock`; -var staleRecoveryLockMs = 1e4; +var startupLock; +var startupLockPath = `${socketPath}.lock`; +var orphanedStartupLockMs = 300 * 1e3; var tcpToken; function native(message) { const body = Buffer.from(JSON.stringify(message)); @@ -252,7 +252,7 @@ var server = node_net.default.createServer((socket) => { function shutdown(code) { for (const socket of clients.keys()) socket.destroy(); if (ownsSocket) removeOwnSocket(); - releaseRecoveryLock(); + releaseStartupLock(); node_process.default.exit(code); } server.on("error", () => { @@ -277,11 +277,12 @@ try { }); const stat = node_fs.default.lstatSync(directory); if (!stat.isDirectory() || stat.uid !== node_process.default.getuid?.() || (stat.mode & 63) !== 0) throw new Error("private socket directory required"); - if (!node_fs.default.existsSync(socketPath)) listenUnix(); + if (!acquireStartupLock()) throw new Error("endpoint busy"); + const existing = lstatIfExists(socketPath); + if (!existing) listenUnix(); else { - const existing = node_fs.default.lstatSync(socketPath); if (!existing.isSocket() || existing.uid !== node_process.default.getuid?.()) throw new Error("endpoint exists"); - reclaimStaleSocket(); + reclaimStaleSocket(existing); } } } catch { @@ -291,6 +292,14 @@ function refuseEndpoint() { node_process.default.stderr.write("Native endpoint setup refused; use a private directory or authenticated TCP\n"); shutdown(1); } +function lstatIfExists(file) { + try { + return node_fs.default.lstatSync(file); + } catch (statError) { + if (statError.code === "ENOENT") return void 0; + throw statError; + } +} function listenUnix() { server.listen(socketPath, () => { try { @@ -301,7 +310,7 @@ function listenUnix() { }; ownsSocket = true; node_fs.default.chmodSync(socketPath, 384); - releaseRecoveryLock(); + releaseStartupLock(); } catch { refuseEndpoint(); } @@ -313,62 +322,93 @@ function removeOwnSocket() { if (boundSocket && current.dev === boundSocket.dev && current.ino === boundSocket.ino) node_fs.default.unlinkSync(socketPath); } catch {} } -function acquireRecoveryLock(retry = true) { +function createStartupLock() { + const staged = `${startupLockPath}.${node_process.default.pid}`; + node_fs.default.writeFileSync(staged, String(node_process.default.pid), { mode: 384 }); try { - node_fs.default.closeSync(node_fs.default.openSync(recoveryLockPath, "wx", 384)); - holdsRecoveryLock = true; + node_fs.default.linkSync(staged, startupLockPath); + const info = node_fs.default.lstatSync(staged); + startupLock = { + dev: info.dev, + ino: info.ino + }; return true; } catch (lockError) { - if (lockError.code !== "EEXIST" || !retry) return false; + if (lockError.code === "EEXIST") return false; + throw lockError; + } finally { try { - const lock = node_fs.default.lstatSync(recoveryLockPath); - if (!lock.isFile() || lock.uid !== node_process.default.getuid?.() || Date.now() - lock.mtimeMs < staleRecoveryLockMs) return false; - node_fs.default.unlinkSync(recoveryLockPath); - } catch { - return false; - } - return acquireRecoveryLock(false); + node_fs.default.unlinkSync(staged); + } catch {} } } -function releaseRecoveryLock() { - if (!holdsRecoveryLock) return; - holdsRecoveryLock = false; +function lockOwnerAlive(pid) { + if (!Number.isInteger(pid) || pid <= 0) return false; try { - node_fs.default.unlinkSync(recoveryLockPath); - } catch {} + node_process.default.kill(pid, 0); + return true; + } catch (signalError) { + return signalError.code === "EPERM"; + } } -function reclaimStaleSocket() { - if (!acquireRecoveryLock()) { - refuseEndpoint(); - return; +function acquireStartupLock() { + if (createStartupLock()) return true; + let lock; + let owner; + try { + lock = node_fs.default.lstatSync(startupLockPath); + if (!lock.isFile() || lock.uid !== node_process.default.getuid?.()) return false; + owner = Number(node_fs.default.readFileSync(startupLockPath, "utf8")); + } catch { + return false; } - let stale; + if (lockOwnerAlive(owner) && Date.now() - lock.mtimeMs < orphanedStartupLockMs) return false; + const moved = `${startupLockPath}.stale.${node_process.default.pid}`; try { - stale = node_fs.default.lstatSync(socketPath); - if (!stale.isSocket() || stale.uid !== node_process.default.getuid?.()) throw new Error("endpoint exists"); + node_fs.default.renameSync(startupLockPath, moved); } catch { - refuseEndpoint(); - return; + return false; } + const movedInfo = lstatIfExists(moved); + if (!movedInfo || movedInfo.dev !== lock.dev || movedInfo.ino !== lock.ino) { + try { + node_fs.default.linkSync(moved, startupLockPath); + } catch {} + try { + node_fs.default.unlinkSync(moved); + } catch {} + return false; + } + try { + node_fs.default.unlinkSync(moved); + } catch {} + return createStartupLock(); +} +function releaseStartupLock() { + const owned = startupLock; + startupLock = void 0; + if (!owned) return; + try { + const current = node_fs.default.lstatSync(startupLockPath); + if (current.dev === owned.dev && current.ino === owned.ino) node_fs.default.unlinkSync(startupLockPath); + } catch {} +} +function reclaimStaleSocket(stale) { const probe = node_net.default.connect(socketPath); probe.once("connect", () => { probe.destroy(); refuseEndpoint(); }); probe.once("error", (probeError) => { - if (probeError.code !== "ECONNREFUSED") { - refuseEndpoint(); - return; - } try { + if (probeError.code !== "ECONNREFUSED") throw new Error("endpoint busy"); const current = node_fs.default.lstatSync(socketPath); if (current.dev !== stale.dev || current.ino !== stale.ino) throw new Error("endpoint changed"); node_fs.default.unlinkSync(socketPath); + listenUnix(); } catch { refuseEndpoint(); - return; } - listenUnix(); }); } var buffer = Buffer.alloc(0); diff --git a/src/extension/background.ts b/src/extension/background.ts index 2f5a196..07ec35e 100644 --- a/src/extension/background.ts +++ b/src/extension/background.ts @@ -1426,6 +1426,7 @@ chrome.alarms.onAlarm.addListener((alarm) => { const port = transport.port; withTimeout(transport.call("ping", {}, HEARTBEAT_TIMEOUT_MS), HEARTBEAT_TIMEOUT_MS, "Native heartbeat") .catch(async (error) => { + if (transport.port !== port) return; const message = error instanceof Error ? error.message : String(error); await runChromeEffect(stopActiveSessions(message)).catch(() => undefined); if (transport.port === port) transport.failPort(message); diff --git a/src/native-host/host.ts b/src/native-host/host.ts index 70f0231..af464c2 100755 --- a/src/native-host/host.ts +++ b/src/native-host/host.ts @@ -22,9 +22,9 @@ const pending = new Map { function shutdown(code: number) { for (const socket of clients.keys()) socket.destroy(); if (ownsSocket) removeOwnSocket(); - releaseRecoveryLock(); + releaseStartupLock(); process.exit(code); } @@ -215,11 +215,12 @@ try { fs.mkdirSync(directory, { recursive: true, mode: 0o700 }); const stat = fs.lstatSync(directory); if (!stat.isDirectory() || stat.uid !== process.getuid?.() || (stat.mode & 0o077) !== 0) throw new Error("private socket directory required"); - if (!fs.existsSync(socketPath)) listenUnix(); + if (!acquireStartupLock()) throw new Error("endpoint busy"); + const existing = lstatIfExists(socketPath); + if (!existing) listenUnix(); else { - const existing = fs.lstatSync(socketPath); if (!existing.isSocket() || existing.uid !== process.getuid?.()) throw new Error("endpoint exists"); - reclaimStaleSocket(); + reclaimStaleSocket(existing); } } } catch { @@ -231,6 +232,14 @@ function refuseEndpoint() { shutdown(1); } +function lstatIfExists(file: string) { + try { return fs.lstatSync(file); } + catch (statError) { + if ((statError as NodeJS.ErrnoException).code === "ENOENT") return undefined; + throw statError; + } +} + // A bind never replaces an existing file, so a racing host fails with // EADDRINUSE instead of taking over this endpoint. function listenUnix() { @@ -240,7 +249,7 @@ function listenUnix() { boundSocket = { dev: bound.dev, ino: bound.ino }; ownsSocket = true; fs.chmodSync(socketPath, 0o600); - releaseRecoveryLock(); + releaseStartupLock(); } catch { refuseEndpoint(); } @@ -256,56 +265,84 @@ function removeOwnSocket() { } catch {} } -function acquireRecoveryLock(retry = true): boolean { +// Every Unix startup, fresh or recovering, holds this lock from the first +// look at the endpoint until the new socket is listening. The lock appears +// atomically with its owner's pid already written, via write then link. +function createStartupLock(): boolean { + const staged = `${startupLockPath}.${process.pid}`; + fs.writeFileSync(staged, String(process.pid), { mode: 0o600 }); try { - fs.closeSync(fs.openSync(recoveryLockPath, "wx", 0o600)); - holdsRecoveryLock = true; + fs.linkSync(staged, startupLockPath); + const info = fs.lstatSync(staged); + startupLock = { dev: info.dev, ino: info.ino }; return true; } catch (lockError) { - if ((lockError as NodeJS.ErrnoException).code !== "EEXIST" || !retry) return false; - try { - const lock = fs.lstatSync(recoveryLockPath); - if (!lock.isFile() || lock.uid !== process.getuid?.() || Date.now() - lock.mtimeMs < staleRecoveryLockMs) return false; - fs.unlinkSync(recoveryLockPath); - } catch { - return false; - } - return acquireRecoveryLock(false); + if ((lockError as NodeJS.ErrnoException).code === "EEXIST") return false; + throw lockError; + } finally { + try { fs.unlinkSync(staged); } catch {} } } -function releaseRecoveryLock() { - if (!holdsRecoveryLock) return; - holdsRecoveryLock = false; - try { fs.unlinkSync(recoveryLockPath); } catch {} +function lockOwnerAlive(pid: number) { + if (!Number.isInteger(pid) || pid <= 0) return false; + try { process.kill(pid, 0); return true; } + catch (signalError) { return (signalError as NodeJS.ErrnoException).code === "EPERM"; } } -// A host killed without cleanup leaves its socket file behind. Recovery runs -// under an exclusive lock so two hosts cannot both unlink the endpoint, and -// only a refused connection proves that no live host owns the socket. -function reclaimStaleSocket() { - if (!acquireRecoveryLock()) { refuseEndpoint(); return; } - let stale: fs.Stats; +// A lock is stale only when its owner is dead. A live host holds it for +// milliseconds, so one older than five minutes is orphaned by pid reuse. +function acquireStartupLock(): boolean { + if (createStartupLock()) return true; + let lock: fs.Stats; + let owner: number; try { - stale = fs.lstatSync(socketPath); - if (!stale.isSocket() || stale.uid !== process.getuid?.()) throw new Error("endpoint exists"); + lock = fs.lstatSync(startupLockPath); + if (!lock.isFile() || lock.uid !== process.getuid?.()) return false; + owner = Number(fs.readFileSync(startupLockPath, "utf8")); } catch { - refuseEndpoint(); - return; + return false; + } + if (lockOwnerAlive(owner) && Date.now() - lock.mtimeMs < orphanedStartupLockMs) return false; + const moved = `${startupLockPath}.stale.${process.pid}`; + try { fs.renameSync(startupLockPath, moved); } catch { return false; } + const movedInfo = lstatIfExists(moved); + if (!movedInfo || movedInfo.dev !== lock.dev || movedInfo.ino !== lock.ino) { + // Another host replaced the stale lock first. Put its lock back. + try { fs.linkSync(moved, startupLockPath); } catch {} + try { fs.unlinkSync(moved); } catch {} + return false; } + try { fs.unlinkSync(moved); } catch {} + return createStartupLock(); +} + +function releaseStartupLock() { + const owned = startupLock; + startupLock = undefined; + if (!owned) return; + try { + const current = fs.lstatSync(startupLockPath); + if (current.dev === owned.dev && current.ino === owned.ino) fs.unlinkSync(startupLockPath); + } catch {} +} + +// A host killed without cleanup leaves its socket file behind. The startup +// lock is held here, so no other host is binding or recovering this path, and +// a refused connection proves that no live host owns the socket. +function reclaimStaleSocket(stale: fs.Stats) { const probe = net.connect(socketPath); probe.once("connect", () => { probe.destroy(); refuseEndpoint(); }); probe.once("error", (probeError: NodeJS.ErrnoException) => { - if (probeError.code !== "ECONNREFUSED") { refuseEndpoint(); return; } try { + if (probeError.code !== "ECONNREFUSED") throw new Error("endpoint busy"); const current = fs.lstatSync(socketPath); if (current.dev !== stale.dev || current.ino !== stale.ino) throw new Error("endpoint changed"); fs.unlinkSync(socketPath); + listenUnix(); } catch { refuseEndpoint(); - return; } - listenUnix(); }); } diff --git a/store/listing.md b/store/listing.md index 0c47f28..e060297 100644 --- a/store/listing.md +++ b/store/listing.md @@ -176,10 +176,10 @@ Keeps the agent's work visible and separate from the user's own tabs. Every tab #### `tabs` -765 / 1,000 characters. +787 / 1,000 characters. ```text -Lets the user's local agent see and use tabs. getUserTabs calls chrome.tabs.query and returns the ID, URL, title, window, position and group of the user's open tabs, leaving out Chrome internal pages, so the agent can pick one to claim. createTab opens an inactive blank tab in a normal window, and claimUserTab adds a tab the agent picked to its session. The extension also groups and ungroups agent tabs, messages its cursor overlay (chrome.tabs.sendMessage), and, when the agent finishes (finalizeTabs), closes only tabs the agent opened. It listens to chrome.tabs.onUpdated, onRemoved and onReplaced to cancel stale actions after a navigation or when a tab closes. Tab URLs and titles are not stored; only tab, group and session IDs are kept for the tab groups. +Lets the user's local agent see and use tabs. getUserTabs calls chrome.tabs.query and returns the ID, URL, title, window, position and group of the user's open tabs, leaving out Chrome internal pages, so the agent can pick one to claim. createTab opens an inactive blank tab in a normal window, and claimUserTab adds a tab the agent picked to its session. The extension also groups and ungroups agent tabs, messages its cursor overlay (chrome.tabs.sendMessage), and, when the agent finishes (finalizeTabs), closes only tabs the agent opened. It listens to chrome.tabs.onUpdated, onRemoved and onReplaced to cancel stale actions after a navigation or when a tab closes. Tab URLs and page titles are not stored; only tab, group and session IDs and group titles are kept for the tab groups. ``` ### Host permission justification @@ -299,7 +299,7 @@ Extension (`background.ts`) → Chrome native messaging (stdio, 4-byte length-pr 1. **In-depth review is likely.** The item requests `debugger`, `` and `tabs`. Google names `` and `tabs` as causes of longer review. 2. **Raw DevTools relay.** `executeCdp` (`background.ts:906-926`) forwards any method except `Target.*` and `Browser.*`, including `Runtime.evaluate`, `Network.getCookies` and `Storage.*`. A reviewer can treat this as executing code that is not in the package, or as broad data access. The remote-code justification discloses it. To remove the risk, change the code to an allowlist, or block `Runtime.evaluate`, `Runtime.compileScript`, `Page.addScriptToEvaluateOnNewDocument` and cookie and storage methods. 3. **Minified bundles.** `dist/extension/*.js` are minified IIFE bundles of about 190 to 245 KB. Google allows minification, but reviewers must be able to understand the code. Static scanners may flag six `eval(` matches per bundle. They are Effect `Micro` object methods named `eval`, not the global `eval`. Consider `build.minify: false` for the store package, and point reviewers to the public source. -4. **Stale docs outside this change.** `README.md` and `docs/DEVELOPER.md` are current. `scripts/install-chrome-control-skill.sh` still downloads from `opzero1/op-chrome`. `skills/chrome-control/SKILL.md` still says "Opzero Chrome", documents the removed `getUserHistory` call and the `onDownloadChange` notification, and shows `client.js` commands with JSON arguments that `client.ts:13-15` rejects. That file ships inside `chrome-control-skill.zip`, which reviewers download. +4. **Stale docs outside this change.** `README.md` and `docs/DEVELOPER.md` are current. `scripts/install-chrome-control-skill.sh` downloads from the `opzero1/browser-control` releases. `skills/chrome-control/SKILL.md` still says "Opzero Chrome" and shows `client.js` commands with JSON arguments that `client.ts:13-15` rejects. The coordinator replaces this file separately. That file ships inside `chrome-control-skill.zip`, which reviewers download. 5. **Leftover host routing.** `host.ts` still accepts `host.subscribeProfileEvents` and routes `onDownloadChange` to subscribed clients (`host.ts:90`, `135-138`), but the extension no longer sends that notification. The code is unreachable and not part of the extension package. A reviewer who reads the host source may still ask about it. 6. **Windows.** A host started by Chrome on Windows exits unless `OPZERO_CHROME_HOST_TOKEN_FILE` is set (`host.ts:199-206`), and the installer never sets it. `transport.ts:37-40` supports only Unix sockets. Do not claim Windows support. The listing says "Other systems need extra manual configuration". 7. **One profile at a time.** All profiles share one socket, and a second host exits when the socket exists (`host.ts:213`). A reviewer with several profiles sees Disconnected in all but one. The reviewer steps say to use one profile. diff --git a/tests/security/host.test.ts b/tests/security/host.test.ts index 281f91f..1840da1 100644 --- a/tests/security/host.test.ts +++ b/tests/security/host.test.ts @@ -1,4 +1,4 @@ -import { spawn } from "node:child_process"; +import { spawn, spawnSync } from "node:child_process"; import fs from "node:fs"; import net from "node:net"; import path from "node:path"; @@ -224,19 +224,43 @@ it("leaves exactly one live host when several recover the same stale endpoint at } }); -it("fails closed while another host holds a fresh recovery lock and clears a stale one", async () => { +function deadPid() { + const child = spawnSync(process.execPath, ["-e", ""]); + return child.pid as number; +} + +it("fails closed while a live process holds the startup lock, for fresh and recovering starts", async () => { + const directory = testTemp(); + cleanup.push(() => fs.rmSync(directory, { recursive: true, force: true })); + const fresh = path.join(directory, "s"); + fs.writeFileSync(`${fresh}.lock`, String(process.pid)); + expect(await spawnHost(fresh).exited).toBe(1); + expect(fs.existsSync(fresh)).toBe(false); + expect(fs.readFileSync(`${fresh}.lock`, "utf8")).toBe(String(process.pid)); + + const stale = await staleEndpoint(); + fs.writeFileSync(`${stale}.lock`, String(process.pid)); + expect(await spawnHost(stale).exited).toBe(1); + expect(fs.lstatSync(stale).isSocket()).toBe(true); + expect(fs.readFileSync(`${stale}.lock`, "utf8")).toBe(String(process.pid)); +}); + +it.each([ + ["a dead owner", (lock: string) => fs.writeFileSync(lock, String(deadPid()))], + ["an orphaned lock whose pid was reused", (lock: string) => { + fs.writeFileSync(lock, String(process.pid)); + const past = new Date(Date.now() - 10 * 60 * 1000); + fs.utimesSync(lock, past, past); + }] +])("takes over the startup lock of %s and recovers the endpoint", async (_name, writeLock) => { const endpoint = await staleEndpoint(); const lock = `${endpoint}.lock`; - fs.writeFileSync(lock, ""); - expect(await spawnHost(endpoint).exited).toBe(1); - expect(fs.lstatSync(endpoint).isSocket()).toBe(true); - expect(fs.existsSync(lock)).toBe(true); - const past = new Date(Date.now() - 60000); - fs.utimesSync(lock, past, past); + writeLock(lock); const recovered = spawnHost(endpoint); await vi.waitFor(async () => expect((await hostInfo(endpoint)).epoch).toBeTypeOf("string")); expect(recovered.child.exitCode).toBeNull(); - expect(fs.existsSync(lock)).toBe(false); + await vi.waitFor(() => expect(fs.existsSync(lock)).toBe(false)); + expect(fs.statSync(endpoint).mode & 0o777).toBe(0o600); }); it("does not remove a socket that replaced its own before it shuts down", async () => { diff --git a/tests/security/native-host-controls.test.ts b/tests/security/native-host-controls.test.ts index bef805d..63b3844 100644 --- a/tests/security/native-host-controls.test.ts +++ b/tests/security/native-host-controls.test.ts @@ -127,3 +127,20 @@ it("replaces a host that stops answering the heartbeat", async () => { b.chrome.alarms.onAlarm.emit(reconnectAlarm); expect(b.ports).toHaveLength(2); }); + +it("ignores a heartbeat that fails after the host was reloaded", async () => { + const b = await background(); + b.port.onMessage.emit(hostProbe); + await vi.waitFor(() => expect(hostState(b)?.state).toBe("connected")); + + b.chrome.alarms.onAlarm.emit(heartbeatAlarm); + await vi.waitFor(() => expect(b.ports[0].sent.some((message: any) => message.method === "ping")).toBe(true)); + await b.popup("RELOAD_NATIVE_HOST"); + b.port.onMessage.emit(hostProbe); + await vi.waitFor(() => expect(hostState(b)?.state).toBe("connected")); + await new Promise(resolve => setTimeout(resolve, 50)); + + expect(b.ports[1].sent.some((message: any) => message.method === "onControlStopped")).toBe(false); + expect(b.ports[1].disconnect).not.toHaveBeenCalled(); + expect(hostState(b)?.state).toBe("connected"); +}); diff --git a/tests/support/background.ts b/tests/support/background.ts index f187867..e19cca5 100644 --- a/tests/support/background.ts +++ b/tests/support/background.ts @@ -16,7 +16,8 @@ export async function background(initialStorage: Record = {}, t let nextTab = 1; const ports: any[] = []; const newPort = () => { - const port = { onMessage: event(), onDisconnect: event(), postMessage: (msg: any) => responses.push(msg), disconnect: vi.fn() }; + const sent: any[] = []; + const port = { onMessage: event(), onDisconnect: event(), postMessage: (msg: any) => { responses.push(msg); sent.push(msg); }, disconnect: vi.fn(), sent }; ports.push(port); return port; }; From 3885b5ba4c891a027b1122b360fb64d1740a9784 Mon Sep 17 00:00:00 2001 From: afif Date: Mon, 28 Sep 2026 16:27:00 +0800 Subject: [PATCH 15/17] Retry observation while a new tab is still on about:blank Chrome refuses to inject into a new tab until its first navigation commits, so waitFor failed about once in nine reviewer runs. The retry covers only that exact message, and it only observes, so no input is replayed. The capture script also stops deleting a socket that another host may still own. --- skills/chrome-control/native-host/transport.js | 16 ++++++++++------ src/native-host/transport.ts | 10 +++++++++- store/capture/launch.sh | 2 +- tests/security/transport.test.ts | 6 +++--- 4 files changed, 23 insertions(+), 11 deletions(-) diff --git a/skills/chrome-control/native-host/transport.js b/skills/chrome-control/native-host/transport.js index 4a8ab84..50bd668 100644 --- a/skills/chrome-control/native-host/transport.js +++ b/skills/chrome-control/native-host/transport.js @@ -80,6 +80,15 @@ function string(value) { if (typeof value !== "string") throw new Error("Invalid string response"); return value; } +var transientObservationErrors = [ + "Page origin not ready or mismatch", + "Frame with ID 0 was removed.", + "Capture or observation blocked: private fields or frames", + "Private document quarantined until cross-document navigation" +]; +function isTransientObservationError(message) { + return transientObservationErrors.includes(message) || message.startsWith("Cannot access contents of url \"about:blank\"."); +} var ChromeTransport = class ChromeTransport { #socket; #next = 0; @@ -219,12 +228,7 @@ var ChromeTransport = class ChromeTransport { const snapshot = await this.observe(page); if ((!expect.text || snapshot.text.includes(expect.text)) && (!expect.url || snapshot.url === expect.url)) return snapshot; } catch (error) { - if (!(error instanceof Error) || ![ - "Page origin not ready or mismatch", - "Frame with ID 0 was removed.", - "Capture or observation blocked: private fields or frames", - "Private document quarantined until cross-document navigation" - ].includes(error.message)) throw error; + if (!(error instanceof Error) || !isTransientObservationError(error.message)) throw error; } if (performance.now() >= deadline) throw new Error("Read-only wait timed out; no input replayed"); await sleep(50); diff --git a/src/native-host/transport.ts b/src/native-host/transport.ts index c4e388b..54ed7cf 100644 --- a/src/native-host/transport.ts +++ b/src/native-host/transport.ts @@ -21,6 +21,14 @@ export type ActionOutcome = { status: "executed" | "not-executed" | "unknown"; r export type UploadOutcome = { status: "attached" | "not-executed" | "unknown"; retry: false; name?: string; mime?: "application/pdf"; size?: number }; export type RecordingReceipt = { path: string | null; directory: string; seconds: number; frames: { file: string; seconds: number; sha256: string }[]; error: string | null; encodeMs: number; captureMs: number; sampleFps: number }; +const transientObservationErrors = ["Page origin not ready or mismatch", "Frame with ID 0 was removed.", "Capture or observation blocked: private fields or frames", "Private document quarantined until cross-document navigation"]; + +// Chrome refuses to inject into a new tab until its first navigation leaves +// about:blank, so an observation made in that window is safe to repeat. +function isTransientObservationError(message: string) { + return transientObservationErrors.includes(message) || message.startsWith('Cannot access contents of url "about:blank".'); +} + export class ChromeTransport { #socket: net.Socket; #next = 0; @@ -116,7 +124,7 @@ export class ChromeTransport { const snapshot = await this.observe(page); if ((!expect.text || snapshot.text.includes(expect.text)) && (!expect.url || snapshot.url === expect.url)) return snapshot; } catch (error) { - if (!(error instanceof Error) || !["Page origin not ready or mismatch", "Frame with ID 0 was removed.", "Capture or observation blocked: private fields or frames", "Private document quarantined until cross-document navigation"].includes(error.message)) throw error; + if (!(error instanceof Error) || !isTransientObservationError(error.message)) throw error; } if (performance.now() >= deadline) throw new Error("Read-only wait timed out; no input replayed"); await sleep(50); diff --git a/store/capture/launch.sh b/store/capture/launch.sh index 45a2eae..8a8c864 100755 --- a/store/capture/launch.sh +++ b/store/capture/launch.sh @@ -9,7 +9,7 @@ source "$(dirname "$0")/env.sh" umask 077 mkdir -p "$BC_PROFILE/NativeMessagingHosts" "$BC_HOSTDIR" "$BC_STATE" "$BC_RAW" chmod 700 "$BC_PROFILE" "$BC_HOSTDIR" "$BC_STATE" "$BC_RAW" -rm -f "$BC_SOCKET" +# The host removes a stale socket itself; never delete one that may be live. EXT_ID=$(python3 "$(dirname "$0")/extension-id.py" "$BC_EXTENSION") echo "$EXT_ID" > "$BC_STATE/extension-id" diff --git a/tests/security/transport.test.ts b/tests/security/transport.test.ts index b402af6..0e27e18 100644 --- a/tests/security/transport.test.ts +++ b/tests/security/transport.test.ts @@ -43,8 +43,8 @@ async function endpoint(observationError: string, check: (client: ChromeTranspor } } -it("waits through a removed main frame by repeating only observation", async () => { - await endpoint("Frame with ID 0 was removed.", async (client, methods) => { +it.each(["Frame with ID 0 was removed.", 'Cannot access contents of url "about:blank". Extension manifest must request permission to access this host.'])("waits through %s by repeating only observation", async observationError => { + await endpoint(observationError, async (client, methods) => { const page = await client.open("https://synthetic.invalid/"); expect((await client.waitFor(page, { text: "Ready" }, 500)).snapshot).toBe("fresh"); expect(methods.filter(method => method === "observePage")).toHaveLength(2); @@ -53,7 +53,7 @@ it("waits through a removed main frame by repeating only observation", async () }); }); -it.each(["Page owner revoked", "Transport closed; outcome unknown; no replay"])("does not hide %s while waiting", async error => { +it.each(["Page owner revoked", "Transport closed; outcome unknown; no replay", 'Cannot access contents of url "https://synthetic.invalid/". Extension manifest must request permission to access this host.'])("does not hide %s while waiting", async error => { await endpoint(error, async (client, methods) => { const page = await client.open("https://synthetic.invalid/"); await expect(client.waitFor(page, { text: "Ready" }, 500)).rejects.toThrow(error); From 51da5b00cd1826b5c7e51e2c125de09b67dcb00b Mon Sep 17 00:00:00 2001 From: afif Date: Mon, 28 Sep 2026 16:34:12 +0800 Subject: [PATCH 16/17] Say that interrupted host starts can leave pid-only files --- docs/PRIVACY.md | 2 +- site/privacy/index.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/PRIVACY.md b/docs/PRIVACY.md index b5708a3..087c1ea 100644 --- a/docs/PRIVACY.md +++ b/docs/PRIVACY.md @@ -67,7 +67,7 @@ These entries stay in Chrome until you remove the extension. The extension does - Windows: `%USERPROFILE%\AppData\Local\opzero-chrome\`, plus the registry key `HKCU\Software\Google\Chrome\NativeMessagingHosts\com.opzero.chrome` It also writes a launcher script in the `native-host` folder and the file `scripts/extension-id.json` inside the helper folder that you unpacked. -- **Host.** The host creates the private folder `~/.opzero-chrome` and a socket file in it, which it deletes when it stops. While it starts, it also holds a small lock file next to the socket that contains only its process ID, and it deletes the lock as soon as the socket is ready. The host keeps no log files. The only messages that it writes to its error output are fixed text with no user data. +- **Host.** The host creates the private folder `~/.opzero-chrome` and a socket file in it, which it normally deletes when it stops. While it starts, it also holds a small lock file next to the socket that contains only its process ID, and it normally deletes the lock as soon as the socket is ready. If a start is interrupted, small files that contain only a process ID can stay next to the socket until the next start cleans them up. The host keeps no log files. The only messages that it writes to its error output are fixed text with no user data. - **Screenshots.** The host passes screenshots to the requesting client in memory. It does not save them. The agent decides whether to keep them. - **Recordings.** When your agent asks for a short recording, the client library in the helper folder (`native-host/transport.js`) saves JPEG frames, a `frames.ffconcat` list, a `capture.json` summary with the time and SHA-256 hash of each frame and, if `ffmpeg` is installed, a `recording.mp4` file. It puts them in a new `tab-video-` folder inside a private folder that your agent tooling chooses, with access restricted to your user account. A recording lasts at most 60 seconds (30 seconds by default) and stops at 100 MB. The files stay until you delete them. diff --git a/site/privacy/index.html b/site/privacy/index.html index 60a61ae..93f80ba 100644 --- a/site/privacy/index.html +++ b/site/privacy/index.html @@ -130,7 +130,7 @@

    Files on your computer

  • Windows: %USERPROFILE%\AppData\Local\opzero-chrome\, plus the registry key HKCU\Software\Google\Chrome\NativeMessagingHosts\com.opzero.chrome
  • It also writes a launcher script in the native-host folder and the file scripts/extension-id.json inside the helper folder that you unpacked. -
  • Host. The host creates the private folder ~/.opzero-chrome and a socket file in it, which it deletes when it stops. While it starts, it also holds a small lock file next to the socket that contains only its process ID, and it deletes the lock as soon as the socket is ready. The host keeps no log files. The only messages that it writes to its error output are fixed text with no user data.
  • +
  • Host. The host creates the private folder ~/.opzero-chrome and a socket file in it, which it normally deletes when it stops. While it starts, it also holds a small lock file next to the socket that contains only its process ID, and it normally deletes the lock as soon as the socket is ready. If a start is interrupted, small files that contain only a process ID can stay next to the socket until the next start cleans them up. The host keeps no log files. The only messages that it writes to its error output are fixed text with no user data.
  • Screenshots. The host passes screenshots to the requesting client in memory. It does not save them. The agent decides whether to keep them.
  • Recordings. When your agent asks for a short recording, the client library in the helper folder (native-host/transport.js) saves JPEG frames, a frames.ffconcat list, a capture.json summary with the time and SHA-256 hash of each frame and, if ffmpeg is installed, a recording.mp4 file. It puts them in a new tab-video- folder inside a private folder that your agent tooling chooses, with access restricted to your user account. A recording lasts at most 60 seconds (30 seconds by default) and stops at 100 MB. The files stay until you delete them.
  • From 2771967f3b61d16a0535d151aa648323af84fb9d Mon Sep 17 00:00:00 2001 From: afif Date: Mon, 28 Sep 2026 16:55:11 +0800 Subject: [PATCH 17/17] Record the 0.2.1 submission and the host lock's known limits --- docs/DEVELOPER.md | 2 ++ docs/RELEASE.md | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/DEVELOPER.md b/docs/DEVELOPER.md index 7ad1bbc..b696bd9 100644 --- a/docs/DEVELOPER.md +++ b/docs/DEVELOPER.md @@ -38,6 +38,8 @@ The popup shows **Connecting** until the host sends its first message, because ` Every Unix host startup holds the lock file `.lock`, from its first look at the socket path until its own socket is listening. The lock holds the owner's process ID. A new host treats it as stale only if that process is dead, or if the lock is older than five minutes, which only happens when a process ID has been reused. While the lock is held, the host removes a socket left by a crashed host only if a connection to it is refused. At shutdown it removes the socket only if it is still the one it bound. A host that cannot take the lock exits, and the extension retries within 30 seconds. +Known limits of this lock: Node.js has no crash-released file lock, so ownership rests on the lock file. Two hosts can still both believe they own the endpoint in three rare cases. The first is an orphaned lock left by an interrupted start, combined with three hosts starting at once. The second is the same orphaned lock, two hosts starting at once, and the filesystem reusing the freed inode. The third is a host suspended for more than five minutes mid-start. A single Chrome profile cannot reach any of these. Use one Chrome profile per socket path. + ## Verify ```sh diff --git a/docs/RELEASE.md b/docs/RELEASE.md index 6007012..5f8ba6f 100644 --- a/docs/RELEASE.md +++ b/docs/RELEASE.md @@ -92,5 +92,5 @@ Then submit for review, from the dashboard or with `action: submit`. Google revi - 0.1.2 was published and is live. - 0.1.3 was rejected on 1 July 2026 for "User data privacy" (reference Purple Nickel). Its privacy policy link pointed to a GitHub file, and Google does not accept a repository page as a privacy policy. The fix was a hosted policy at `https://browser-control.pages.dev/privacy/`. -- 0.2.1 renames the item from Chrome Control to Browser Control, as the branding guidelines require, and drops the unused `history` and `downloads` permissions. +- 0.2.1 renames the item from Chrome Control to Browser Control, as the branding guidelines require, and drops the unused `history` and `downloads` permissions. It was submitted for review on 28 September 2026 with automatic publishing after approval, from the extension source at `5717395`. - The old `CHROME_CLIENT_ID`, `CHROME_CLIENT_SECRET` and `CHROME_REFRESH_TOKEN` secrets belong to an OAuth client whose consent screen stayed in testing mode, so its refresh tokens expired after seven days. The workflow no longer uses them.