From 17e2ab8f1ae6c3e8efa1e67df38032f1bb1d3929 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 27 Jun 2026 03:15:11 +0000 Subject: [PATCH 01/17] feat: add Agrifine browser extension scaffold (Phase 1) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Manifest V3 Chrome extension with a persistent side-panel dashboard for farm data management. Includes: - Background service worker that proxies all Anthropic API calls (API key stored in chrome.storage.session, never in content scripts) - Content script that extracts page text for reading-list summarisation - Sidebar UI with bottom tab bar and settings panel (API key entry) - Five module stubs wired to live storage: 1. ReadingList — save pages with AI summary + topic tagging 2. DataIngest — drag-and-drop CSV/Excel/PDF → AI-structured JSON 3. FieldProfile — per-field cards with CLU, acres, soil, coordinates 4. Dashboard — unified filterable view + natural-language AI query bar 5. CarbonEstimator — Phase 7 stub with feature preview - Shared storage schema (chrome.storage.local) with context-bundle builder for passing reading-list + field data as AI system context - Tailwind CSS + Webpack 5 build pipeline; builds successfully Co-Authored-By: Claude Sonnet 4.6 Claude-Session: https://claude.ai/code/session_01KBD2dN2KEjzz3UQFa9hEpu --- agrifine-extension/.babelrc | 8 + agrifine-extension/.gitignore | 3 + agrifine-extension/manifest.json | 45 + agrifine-extension/package-lock.json | 5282 +++++++++++++++++ agrifine-extension/package.json | 30 + agrifine-extension/postcss.config.js | 6 + agrifine-extension/public/icons/icon128.png | Bin 0 -> 306 bytes agrifine-extension/public/icons/icon16.png | Bin 0 -> 79 bytes agrifine-extension/public/icons/icon32.png | Bin 0 -> 99 bytes agrifine-extension/public/icons/icon48.png | Bin 0 -> 123 bytes agrifine-extension/src/background/index.js | 42 + agrifine-extension/src/content/index.js | 25 + .../src/modules/carbon-estimator/index.js | 56 + .../src/modules/dashboard/index.js | 183 + .../src/modules/data-ingest/index.js | 234 + .../src/modules/field-profile/index.js | 179 + .../src/modules/reading-list/index.js | 154 + agrifine-extension/src/sidebar/index.js | 90 + agrifine-extension/src/sidebar/sidebar.css | 60 + agrifine-extension/src/sidebar/sidebar.html | 94 + agrifine-extension/src/utils/api.js | 66 + agrifine-extension/src/utils/storage.js | 136 + agrifine-extension/tailwind.config.js | 35 + agrifine-extension/webpack/webpack.config.js | 57 + 24 files changed, 6785 insertions(+) create mode 100644 agrifine-extension/.babelrc create mode 100644 agrifine-extension/.gitignore create mode 100644 agrifine-extension/manifest.json create mode 100644 agrifine-extension/package-lock.json create mode 100644 agrifine-extension/package.json create mode 100644 agrifine-extension/postcss.config.js create mode 100644 agrifine-extension/public/icons/icon128.png create mode 100644 agrifine-extension/public/icons/icon16.png create mode 100644 agrifine-extension/public/icons/icon32.png create mode 100644 agrifine-extension/public/icons/icon48.png create mode 100644 agrifine-extension/src/background/index.js create mode 100644 agrifine-extension/src/content/index.js create mode 100644 agrifine-extension/src/modules/carbon-estimator/index.js create mode 100644 agrifine-extension/src/modules/dashboard/index.js create mode 100644 agrifine-extension/src/modules/data-ingest/index.js create mode 100644 agrifine-extension/src/modules/field-profile/index.js create mode 100644 agrifine-extension/src/modules/reading-list/index.js create mode 100644 agrifine-extension/src/sidebar/index.js create mode 100644 agrifine-extension/src/sidebar/sidebar.css create mode 100644 agrifine-extension/src/sidebar/sidebar.html create mode 100644 agrifine-extension/src/utils/api.js create mode 100644 agrifine-extension/src/utils/storage.js create mode 100644 agrifine-extension/tailwind.config.js create mode 100644 agrifine-extension/webpack/webpack.config.js diff --git a/agrifine-extension/.babelrc b/agrifine-extension/.babelrc new file mode 100644 index 0000000000..7dc2569887 --- /dev/null +++ b/agrifine-extension/.babelrc @@ -0,0 +1,8 @@ +{ + "presets": [ + ["@babel/preset-env", { + "targets": { "chrome": "109" }, + "modules": false + }] + ] +} diff --git a/agrifine-extension/.gitignore b/agrifine-extension/.gitignore new file mode 100644 index 0000000000..a567f9e13f --- /dev/null +++ b/agrifine-extension/.gitignore @@ -0,0 +1,3 @@ +node_modules/ +dist/ +*.map diff --git a/agrifine-extension/manifest.json b/agrifine-extension/manifest.json new file mode 100644 index 0000000000..a24c3d3f61 --- /dev/null +++ b/agrifine-extension/manifest.json @@ -0,0 +1,45 @@ +{ + "manifest_version": 3, + "name": "Agrifine", + "version": "0.1.0", + "description": "Farm data dashboard — reading list, data ingestion, field profiles, and AI-powered insights.", + "permissions": [ + "storage", + "sidePanel", + "activeTab", + "scripting", + "tabs" + ], + "host_permissions": [ + "" + ], + "background": { + "service_worker": "dist/background.js", + "type": "module" + }, + "content_scripts": [ + { + "matches": [""], + "js": ["dist/content.js"], + "run_at": "document_idle" + } + ], + "side_panel": { + "default_path": "dist/sidebar.html" + }, + "action": { + "default_title": "Open Agrifine", + "default_icon": { + "16": "icons/icon16.png", + "32": "icons/icon32.png", + "48": "icons/icon48.png", + "128": "icons/icon128.png" + } + }, + "icons": { + "16": "icons/icon16.png", + "32": "icons/icon32.png", + "48": "icons/icon48.png", + "128": "icons/icon128.png" + } +} diff --git a/agrifine-extension/package-lock.json b/agrifine-extension/package-lock.json new file mode 100644 index 0000000000..1bc27def3d --- /dev/null +++ b/agrifine-extension/package-lock.json @@ -0,0 +1,5282 @@ +{ + "name": "agrifine-extension", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "agrifine-extension", + "version": "0.1.0", + "dependencies": { + "papaparse": "^5.4.1", + "pdfjs-dist": "^4.2.67", + "xlsx": "^0.18.5" + }, + "devDependencies": { + "@babel/core": "^7.24.0", + "@babel/preset-env": "^7.24.0", + "autoprefixer": "^10.4.19", + "babel-loader": "^9.1.3", + "copy-webpack-plugin": "^12.0.2", + "css-loader": "^7.1.2", + "mini-css-extract-plugin": "^2.9.0", + "postcss": "^8.4.38", + "postcss-loader": "^8.1.1", + "tailwindcss": "^3.4.3", + "webpack": "^5.91.0", + "webpack-cli": "^5.1.4" + } + }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.29.7", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz", + "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", + "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helpers": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.7.tgz", + "integrity": "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.29.7.tgz", + "integrity": "sha512-OoK6239jHPuSQOoS0kfTVKn0b/rVTk0seKq4Gd2UMLtmOVLjDC0ki3e+c90Trqv2gMfvJFqkiljrr568+qddiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", + "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.29.7.tgz", + "integrity": "sha512-IY3ZD9Tmooqr3TUhc3DUWxiuo8xx1DWLhd5M7hQ+ZWJamqM2BbalrBJb2MisSLoYorOj75U03qULCxQTY9r3hg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.29.7", + "@babel/helper-member-expression-to-functions": "^7.29.7", + "@babel/helper-optimise-call-expression": "^7.29.7", + "@babel/helper-replace-supers": "^7.29.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7", + "@babel/traverse": "^7.29.7", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.29.7.tgz", + "integrity": "sha512-907Uymvqgg1dwUA+7IGwFAOSYzQOuzPXKNJ1yxzwPffzkYFg2q2eHi1fIOs6sXkG9NbIUMunnUlkYsfRFNvomg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.29.7", + "regexpu-core": "^6.3.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-define-polyfill-provider": { + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.8.tgz", + "integrity": "sha512-47UwBLPpQi1NoWzLuHNjRoHlYXMwIJoBf7MFou6viC/sIHWYygpvr0B6IAyh5sBdA2nr2LPIRww8lfaUVQINBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", + "debug": "^4.4.3", + "lodash.debounce": "^4.0.8", + "resolve": "^1.22.11" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz", + "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.29.7.tgz", + "integrity": "sha512-j+7JYmk1JYDtACIGj0QJqqWZjoUpMoEikQGADMaHgCMCSDqd2+P32rfcibUNrGOMWrlzK1WJBdxrB3JJQZwWtg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", + "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", + "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.29.7.tgz", + "integrity": "sha512-+kmGVjcT9RGYzoDwdwEqEvGgKe3BYq+O1iGzjFubaNgZHwYHP6lsF2Yghf4kEuv9BV7tYDZ913aBW9am6YKong==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz", + "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-remap-async-to-generator": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.29.7.tgz", + "integrity": "sha512-16AMiW26DbXWBbr3B8wNozKM0ydMLB892vaOaJW/fPJdnT8vJk5sdkQcU/isqUxyCE0cEoa8wZOcbgDuC4b6Og==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.29.7", + "@babel/helper-wrap-function": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.29.7.tgz", + "integrity": "sha512-atfGXWSeCiF4DnKZIfmJfQRkSw9b9gNNXR1kqKjbhG4pGYCOnkp8OcTB8E3NXjBu8NpheSnOeNKz8KT7UNFTmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-member-expression-to-functions": "^7.29.7", + "@babel/helper-optimise-call-expression": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.29.7.tgz", + "integrity": "sha512-brcMGQaVzIeUb+6/bs1Av0f8YuNNjKY2JyvfRCsFuFsdKccEQ5Ges2y74D74NZ1Rz8lKJ9ksJkfqwQFJ/iNEyQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", + "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-wrap-function": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.29.7.tgz", + "integrity": "sha512-iES0Skag9ERIF68aXadpO6dbXa03mNWK3sEqJaMnLNs/eC3l0lkImdfoy6Y09/SfkpawdAB4RjQ7PVA7TcVGdw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.29.7", + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz", + "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz", + "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.7" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.29.7.tgz", + "integrity": "sha512-j8SrR0zLZrRsC09DlszEx8FpMiwukKffYXMK0d5LmOglO7vGG6sz/BR/20yHqWH+Lnn31JTt2PE3hIWNgM2J6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-class-field-initializer-scope": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.29.7.tgz", + "integrity": "sha512-r8j8escF+U2FUHo0KOhPUdMzUO+jp9fInva6+ACVAF3Y97Ev+5iNZwiqTghmzNeWwDkOPlYuTcfb1vDaoZKmAQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.29.7.tgz", + "integrity": "sha512-GE1TFSiuFeGsCxmYXZl8HwoPrVlwe4rHPFE8weieGKZqnDORK+Ar3vgWMgW+AOxQ6/2TgLSKx9p6W7O4rC6qgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-rest-destructuring-rhs-array": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-rest-destructuring-rhs-array/-/plugin-bugfix-safari-rest-destructuring-rhs-array-7.29.7.tgz", + "integrity": "sha512-oBNVCvnO5tND+xSopWvV8WNGfpTfgP4Zr/YXXSj8zfmcPktp5Ku/aZlsIowgSD4fjmgHn6sGmB9APVsU5zOdhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.29.7.tgz", + "integrity": "sha512-QQt9qKHZ2sg/kivaLr7lnQr8HVrQDdBNSfCsTjiDxRuX/K5ORyKq+Bu8Xr0cDE3Dfkv0cw28Ve0EKyKMvulkOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7", + "@babel/plugin-transform-optional-chaining": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.13.0" + } + }, + "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.29.7.tgz", + "integrity": "sha512-pn6QacGLgvCcwc+syUhKE/qSjV2D1IHDB84RNxWYSt1mW3K/SCtjinZ2p0cETJxAWBjPy3K/1lHwG5BjjPxNlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-proposal-private-property-in-object": { + "version": "7.21.0-placeholder-for-preset-env.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", + "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-assertions": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.29.7.tgz", + "integrity": "sha512-/An1OCBN93thpBAGyfsK2pcf0jvju1SAtKkL2Ny++B5Sy6sqgzXDQH1cZxWbF96Wuk+bn41MDA9bLd4VVAw6rw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.29.7.tgz", + "integrity": "sha512-zGYcYfq/WmZ4V+kBIXQon9dSSc8ircGZqw9ZaNhhGj9nZkeBu1jHLBDQqYYi5WA9uawvA2sIMbry2nCFhf5Djg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-unicode-sets-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", + "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-arrow-functions": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.29.7.tgz", + "integrity": "sha512-N7zArUXWzAMzm+/N0uPBeVB3Fam5lMxtUwMmDK5f/IBBS7a7p1qeUoxd/6CckXoxUdgsntq1Dh8xNW06maZbDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-generator-functions": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.29.7.tgz", + "integrity": "sha512-d98gXZkgswvkyohMBABkhm3GeXhYj8psWfwQ2C7gtfrKGTykQa/iOIi+JJhwMjPlZ6Vm2XN+DCf3Es1EoG4ZLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-remap-async-to-generator": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-to-generator": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.29.7.tgz", + "integrity": "sha512-pcUb2SS+RMo9TWVBwKGI5ShtoG7R+zBsFmCKDa6fe8c+hPr3XJlZgoE5j6i8W7gDjhyvy+85vmYexanvXh3d1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-remap-async-to-generator": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoped-functions": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.29.7.tgz", + "integrity": "sha512-cUSmjh72N+rN4PrkFlN1dJwNCwjVp5d38/CQrEsFggkD10UiFlBFgdH3tv5dNsLuHY+3S8db2xCHjhZcv5WgvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoping": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.29.7.tgz", + "integrity": "sha512-ONyr4+AZhKh8yKWInVxU9AXA9EbsyeLcL6V0dJy6M2/62vuvpGm29zzuymbTpdc451GEpDIdAyPLP3r+P61yKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-class-properties": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.29.7.tgz", + "integrity": "sha512-GtcpjFvanPfzNQi3eTitsCqtRRmmqzpy/A+yhTR1HaZo1Ly3EA8ZXxlPyHdR8/IuRMYc3E4wdGBewB2QKQjAaA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-class-static-block": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.29.7.tgz", + "integrity": "sha512-kibJgmEdX2iMwsHY2tSZNDgj8PwIlCQz7FK9KuGKO8zsuoUwSEhoNnNVp/emKWrbY4HeO6kkXfdMqRKKKXBm2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.12.0" + } + }, + "node_modules/@babel/plugin-transform-classes": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.29.7.tgz", + "integrity": "sha512-qV0OGGBVacduzQHE649JyCneOFI/maT+YKsO+K4Yi3xv2wTPNjM/W2o2gdzMwEAZz7fXNTHAe0NcSg30bIN69g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-globals": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-replace-supers": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-computed-properties": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.29.7.tgz", + "integrity": "sha512-RK7/IyU5phpuCdBAuig5VkzG/EnbDaui5SQGdU9BFrHdV+mV4cUjLMQ9lJDjLNtWHsqtiefpGZUXQP2BiTYMsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/template": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-destructuring": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.29.7.tgz", + "integrity": "sha512-iPX8aD6H9zV5s7ZsqTdNocPN/MGQ5sSMnElKrktxjJRMnB2jN/1p2+R7GkfD6CAYoVFqy5A4XnSIUeGgJzIWpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-dotall-regex": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.29.7.tgz", + "integrity": "sha512-3qc18hsD2RdZiyJNDNc7HQpv6xbncwh8FYtxNFFzclSyh/trPD9KkVR9BDECUjDLvb7yJVF15GfYUuC+LMkkiQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-duplicate-keys": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.29.7.tgz", + "integrity": "sha512-6IvRRriEMqnBwD6chtxdLpMYCHWEzN+oL5cyQtjykya19UgzbmKhxmhZgKC/LHxS2nYr9Q/qYPZ5Lr6jOL9+yQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-duplicate-named-capturing-groups-regex": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.29.7.tgz", + "integrity": "sha512-2wiIyo2BjtgU7HufSeDnL9L2O7zr8jmhFKuSr65VpRkUiRKRNpb0mdlk56+XPPKoIrfHqzbMuglDvZun0RISsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-dynamic-import": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.29.7.tgz", + "integrity": "sha512-giOlEm/EFjfjr+te9NsdjkUo2v4f8rS/SXPumRVHAtbNcyNlvtREkU1dZzaIDclNpnaVhlCqRdFKhJBjBikzLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-explicit-resource-management": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-explicit-resource-management/-/plugin-transform-explicit-resource-management-7.29.7.tgz", + "integrity": "sha512-Rstj7coNz8sE+7Ju7ihpHLI564lsK5pUpNNlvptCIC/16E/S5hbl6n3kESPKdNRmqEWlpn5xpS5Q2dvXBsySLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/plugin-transform-destructuring": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-exponentiation-operator": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.29.7.tgz", + "integrity": "sha512-zFpMOTLZBdW5LfObqcSbL6kefg4R4eLdmvS0wbN9M6D5Mym/sKm9toOoWyVOa+xDjvCnuWcHls2YonXwHvH3CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-export-namespace-from": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.29.7.tgz", + "integrity": "sha512-24B2nOy2TeJSMheqwPD4DDQOV/elLSIlKxjZt4i05H5AgdPdWR3n18HnNrcJ+j76WJd9gbwb9jPjNYUy6RautA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-for-of": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.29.7.tgz", + "integrity": "sha512-zeSIHh0+E1Um1WJRXCFlHQYu2ieJNdivLLjlBEp+dIBu3S51n+SZZmIXjxnItw6pz56Cn+KvK68BIBVsxq2JiQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-function-name": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.29.7.tgz", + "integrity": "sha512-otRWaHXE6fbAGkePvaj/kvs3HsqXfPhlnzwSOlnFgbqCPMd975dW+4wZ00WFBt+/YlBGcJwNrARQTOJOb4ZrIg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-json-strings": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.29.7.tgz", + "integrity": "sha512-RRnE2+eon1rJAq8MnoF1b5kTpY1vU88twHcvcKMrsqP/jxIRqDVs9iJB5fqPuqyeFAW0wJo4MlUIPpQCq/aRsg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-literals": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.29.7.tgz", + "integrity": "sha512-DZ/oLP21ZuWx1vKqnoNv6/tvEK48AQOBRai40CX9dTjGluvT/YZCyY3rryDtyUqCEoyNroy5KKPwX2iQCiRvyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-logical-assignment-operators": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.29.7.tgz", + "integrity": "sha512-A0H91hh6W8MFRkp5TqJmMr39jzGD1A1E1Ysiv2O06Sfbhkapm+XyIzxWCEh5kqwOZ1/8QZ0dY3SeQ7XBqfJd5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-member-expression-literals": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.29.7.tgz", + "integrity": "sha512-hl1kwFZCCiDyfH25Xmco9jTrkPgnS9pmOzSG7W5I4SaGbLeqKv417hcU2RKmaxoPEgsoJh7ZPOrnPGq99bHoUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-amd": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.29.7.tgz", + "integrity": "sha512-fxtQoH3m5ywUSIfaH0FGCzWu4McsYon5bD3K4XnskC7f+OyQMj7rsOMi4NvvmJ83WwBAg4UCe+ov4VZlqEvyew==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.29.7.tgz", + "integrity": "sha512-j0vCldybPC5b5dwCQOJ21uKtHzt7hxLygJTg9eF1ScfaikEDNfzn94XoW5Fi+seBR0nCyL23xaBFFkq7dTM8XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-systemjs": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.29.7.tgz", + "integrity": "sha512-TM2ZcQLoG2/y4HODiStCo10DibYhWhGWAwVv+EQKmG/7GFl0N+AAmUiXOMKM+aiJ9XBJ9AHVZBvTzMnJ2sM3cQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-umd": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.29.7.tgz", + "integrity": "sha512-B4UkaTK3QpgCwJnrxKfMPKdo92CN7OKXAlpAAnM3UPu0Q0lCCk57ylA9AJbRy2v8dDKOPAAWcoR6CMyeoHwRCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.29.7.tgz", + "integrity": "sha512-vuFoLwr4qnv2xbZ16SQd6uPcH5FNrLHhk/Jzo++0XJFcaDsr4gjJVg6j398oMHiC+83k/GiBzviwF5KBJkPUtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-new-target": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.29.7.tgz", + "integrity": "sha512-fEo41GmsOUhOBlw8ioo6zvjX5Xc2Lqkzlyfqbpsk3eB6TReV18uhxZ0esfEokVbY2+PVJAQHNKxER6lGrzNd3A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.29.7.tgz", + "integrity": "sha512-idmp1dFaekP9GbcMvG24Kvw2BfhFZjHnNJCkV4WuIY4PskJzwI3f1N5OdgYke38T7rftO6ERulFRn2cFeZwRkg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-numeric-separator": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.29.7.tgz", + "integrity": "sha512-zR7fv/z14OjgHl4AgRtkDBvBMhIzCxqV/qN/2BCRC7LjFwvuzjYe7gDWxC4Wl/SNsLM6SE1IWvRPYMgSJaUvNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-rest-spread": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.29.7.tgz", + "integrity": "sha512-Ld98jn4c0smUywL57m7SgsHq3OpThOa6LqZJif3G6jYOovPleoFhVrBJ1WegRApSFB2wu4+RelAj9AC9G08Z4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/plugin-transform-destructuring": "^7.29.7", + "@babel/plugin-transform-parameters": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-super": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.29.7.tgz", + "integrity": "sha512-Ea/diGcw0twB5IlZPO5sgET6fJsLJqPABqTuFWIR+iMPGPZJkATEIWx0wa+aEQ5UY1CBQyP/gkAiLEqn1vBiQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-replace-supers": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-optional-catch-binding": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.29.7.tgz", + "integrity": "sha512-sLsyndxK2VwX6yNUOakMb7Sh553ZTe/vVM1XJ+9Z5aW1ytsc8xOIwmyk05NNjN60vkc5/KqoTH6hB4V41LJhng==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-optional-chaining": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.29.7.tgz", + "integrity": "sha512-6GM1dhvK3gNODkXcEcMCOLEDCLSoZ/sBbro2Ax8HURyasQ4NshagQixkRFdh5niI6E4gmA/jYI/4aT7rRos3ZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-parameters": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.29.7.tgz", + "integrity": "sha512-ZDOBqV/qLYJI0YElr8DcENEyARsFQeESqWXH6gZlghYXuPPjvweuDhP4VyEi4BlUBlLRFZVjxoZDMjxhLW766g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-private-methods": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.29.7.tgz", + "integrity": "sha512-/6Rz4DK1ETDEM/bWHsPHcaEe7ZaT1EqSXjtSP/L0DijOYuaUhiRiOKcwpZ8P7zR4xXEHc2ITdiCgBm9Tpyv9ug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-private-property-in-object": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.29.7.tgz", + "integrity": "sha512-+BNo06dnrzdNNqCm1X6YUaVv0DKk8Q+JYcoZfOkLhYWNCXzlwTSRq8zGWayT1csjcpNXV9CQTBRRbmTLZac5cA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.29.7", + "@babel/helper-create-class-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-property-literals": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.29.7.tgz", + "integrity": "sha512-bOMRLQuI0A5ZqHq3OWJ89/rXpJ/NJrbVhXiP4zwPGMs6kpcVsuTUNjwoE30K0Qm3mf48a/TnRYYD6vPNqcg6jA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-regenerator": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.29.7.tgz", + "integrity": "sha512-rNNFV0DBAJp988xW2DOntfDoYn1eR8GGF5AT5vYc+rjyfaQkM242c9tZUHHPe7KYaiJizXPWhQTzzdbXySyhBw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-regexp-modifiers": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.29.7.tgz", + "integrity": "sha512-mB5Fs0VWrJ42ZCmc8114v60qetdaUVNkj9PmSZRmanCZM3S9hm0CFRLjRmYIsuXav14l2jvZ+4T8iiCGnhj3nQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-reserved-words": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.29.7.tgz", + "integrity": "sha512-5+YhdpVgmfSmwZyLMftfaiffLRMHjzIRHFHHLdibcSyJm2pasMrKHrO3Ptrt2DRshjvpgjEJJ1zVW14WPq/6QA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-shorthand-properties": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.29.7.tgz", + "integrity": "sha512-I+WYbGBAiCn7nA6xBrlgPH+MB7HWb4u8pv5S0Pv7OtwNvIFvCCb24YlttKEeUFVurfBCEaOTnuhlqsb7f0Z5Dg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-spread": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.29.7.tgz", + "integrity": "sha512-/u5K1QWada7tbYNqTjMh96718g9NTwh9tfPJMsSmVsQwGT447FskV+KcfeXkXq2GWki4EM/MuTdmBec+hOuVTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-sticky-regex": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.29.7.tgz", + "integrity": "sha512-BCHzNYJGe9l7EpwwDBN/ztlL2NYFFq8hp9ddjtUEM9f2O7S7kKV/lL6Fwo7IF7NSkYhPK2vO+86nIGltA90MsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-template-literals": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.29.7.tgz", + "integrity": "sha512-NCSEJ4sLFU2gqAub45HYh4fus2yQ36rr6ei6vpU7NdoJqCpxvEG8E6eJpscGyXP3VHD2Ny+fSXr04k1hoUrFqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typeof-symbol": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.29.7.tgz", + "integrity": "sha512-223mNGoTkBiTEWFoK+Q6Go3tueMRclO8vxxxxquNCYuNI4jWOofFKJRRDu6SDrB8Sgo1UEGW9T4GAQ8ZyRso1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-escapes": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.29.7.tgz", + "integrity": "sha512-jCfXxSjf94lf4E0hKE0AByxF6F3/pVFqRdUUNkDJhsY0m1ZKjnN6ZYyMeHNpzflxb/0q5b7t3p+BE+SLF1WOtA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-property-regex": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.29.7.tgz", + "integrity": "sha512-OgZ+zoAJgZLUCunsTRQ5LAjOywDv5zzZ2/hQ5aMw1pGXyY2rtE8/chXYUmu3AlVHKpm10KEdG9aMwbI/K76ZGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-regex": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.29.7.tgz", + "integrity": "sha512-7D/x/23/d/3VqZ0QA+LGbZMlGwZjztBygSWWWsfTPoQ1oQ6Q1P6Mr3d0kk42XabyUVw+fha3LqdRsFqeKqvCyA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-sets-regex": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.29.7.tgz", + "integrity": "sha512-BLOhLht9DOJwIxlmp91wHvkXv1lguuHS3/FwUO8HL1H0u8s4hR1gASVFyilu9iGtcTRYqjTZmlsFFeQletntEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/preset-env": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.29.7.tgz", + "integrity": "sha512-GYzX36n1nsciIb0uyH0GHwxwtNwPQIcpxSeiVLDtG/B7jB5xXgchnmL1f/jCX5o+pwnaDBtO60ONSJhEBJfxYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.29.7", + "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.29.7", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.29.7", + "@babel/plugin-bugfix-safari-rest-destructuring-rhs-array": "^7.29.7", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.29.7", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.29.7", + "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", + "@babel/plugin-syntax-import-assertions": "^7.29.7", + "@babel/plugin-syntax-import-attributes": "^7.29.7", + "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", + "@babel/plugin-transform-arrow-functions": "^7.29.7", + "@babel/plugin-transform-async-generator-functions": "^7.29.7", + "@babel/plugin-transform-async-to-generator": "^7.29.7", + "@babel/plugin-transform-block-scoped-functions": "^7.29.7", + "@babel/plugin-transform-block-scoping": "^7.29.7", + "@babel/plugin-transform-class-properties": "^7.29.7", + "@babel/plugin-transform-class-static-block": "^7.29.7", + "@babel/plugin-transform-classes": "^7.29.7", + "@babel/plugin-transform-computed-properties": "^7.29.7", + "@babel/plugin-transform-destructuring": "^7.29.7", + "@babel/plugin-transform-dotall-regex": "^7.29.7", + "@babel/plugin-transform-duplicate-keys": "^7.29.7", + "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.29.7", + "@babel/plugin-transform-dynamic-import": "^7.29.7", + "@babel/plugin-transform-explicit-resource-management": "^7.29.7", + "@babel/plugin-transform-exponentiation-operator": "^7.29.7", + "@babel/plugin-transform-export-namespace-from": "^7.29.7", + "@babel/plugin-transform-for-of": "^7.29.7", + "@babel/plugin-transform-function-name": "^7.29.7", + "@babel/plugin-transform-json-strings": "^7.29.7", + "@babel/plugin-transform-literals": "^7.29.7", + "@babel/plugin-transform-logical-assignment-operators": "^7.29.7", + "@babel/plugin-transform-member-expression-literals": "^7.29.7", + "@babel/plugin-transform-modules-amd": "^7.29.7", + "@babel/plugin-transform-modules-commonjs": "^7.29.7", + "@babel/plugin-transform-modules-systemjs": "^7.29.7", + "@babel/plugin-transform-modules-umd": "^7.29.7", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.29.7", + "@babel/plugin-transform-new-target": "^7.29.7", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.29.7", + "@babel/plugin-transform-numeric-separator": "^7.29.7", + "@babel/plugin-transform-object-rest-spread": "^7.29.7", + "@babel/plugin-transform-object-super": "^7.29.7", + "@babel/plugin-transform-optional-catch-binding": "^7.29.7", + "@babel/plugin-transform-optional-chaining": "^7.29.7", + "@babel/plugin-transform-parameters": "^7.29.7", + "@babel/plugin-transform-private-methods": "^7.29.7", + "@babel/plugin-transform-private-property-in-object": "^7.29.7", + "@babel/plugin-transform-property-literals": "^7.29.7", + "@babel/plugin-transform-regenerator": "^7.29.7", + "@babel/plugin-transform-regexp-modifiers": "^7.29.7", + "@babel/plugin-transform-reserved-words": "^7.29.7", + "@babel/plugin-transform-shorthand-properties": "^7.29.7", + "@babel/plugin-transform-spread": "^7.29.7", + "@babel/plugin-transform-sticky-regex": "^7.29.7", + "@babel/plugin-transform-template-literals": "^7.29.7", + "@babel/plugin-transform-typeof-symbol": "^7.29.7", + "@babel/plugin-transform-unicode-escapes": "^7.29.7", + "@babel/plugin-transform-unicode-property-regex": "^7.29.7", + "@babel/plugin-transform-unicode-regex": "^7.29.7", + "@babel/plugin-transform-unicode-sets-regex": "^7.29.7", + "@babel/preset-modules": "0.1.6-no-external-plugins", + "babel-plugin-polyfill-corejs2": "^0.4.15", + "babel-plugin-polyfill-corejs3": "^0.14.0", + "babel-plugin-polyfill-regenerator": "^0.6.6", + "core-js-compat": "^3.48.0", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-modules": { + "version": "0.1.6-no-external-plugins", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", + "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@babel/template": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", + "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.7.tgz", + "integrity": "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-globals": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz", + "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@discoveryjs/json-ext": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", + "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.11", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", + "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@napi-rs/canvas": { + "version": "0.1.100", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas/-/canvas-0.1.100.tgz", + "integrity": "sha512-xglYA6q3XO5P3BNJYxVZ1IV7DLVjp1Py6nwag88YntrS+3vKHyYcMqXVS4ZztJmwz2uGvz1FWhI/4LgbR5uQDA==", + "license": "MIT", + "optional": true, + "workspaces": [ + "e2e/*" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "optionalDependencies": { + "@napi-rs/canvas-android-arm64": "0.1.100", + "@napi-rs/canvas-darwin-arm64": "0.1.100", + "@napi-rs/canvas-darwin-x64": "0.1.100", + "@napi-rs/canvas-linux-arm-gnueabihf": "0.1.100", + "@napi-rs/canvas-linux-arm64-gnu": "0.1.100", + "@napi-rs/canvas-linux-arm64-musl": "0.1.100", + "@napi-rs/canvas-linux-riscv64-gnu": "0.1.100", + "@napi-rs/canvas-linux-x64-gnu": "0.1.100", + "@napi-rs/canvas-linux-x64-musl": "0.1.100", + "@napi-rs/canvas-win32-arm64-msvc": "0.1.100", + "@napi-rs/canvas-win32-x64-msvc": "0.1.100" + } + }, + "node_modules/@napi-rs/canvas-android-arm64": { + "version": "0.1.100", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-android-arm64/-/canvas-android-arm64-0.1.100.tgz", + "integrity": "sha512-hjhCKhntPv9+t4ckHymdx0phYNcVW+GKQR6Lzw2zE+pOVjOplSmtx9nNNknTjbEDLcuLZqA1y8ufKg1XfgftzQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@napi-rs/canvas-darwin-arm64": { + "version": "0.1.100", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-darwin-arm64/-/canvas-darwin-arm64-0.1.100.tgz", + "integrity": "sha512-2PcswRaC7Ly645DGt88///zuFDhJxJYdKAs1uU3mfk1atYkXufgcgLfBpk6Tm12nCQBaNt1wpybuPZ4qOhTo8A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@napi-rs/canvas-darwin-x64": { + "version": "0.1.100", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-darwin-x64/-/canvas-darwin-x64-0.1.100.tgz", + "integrity": "sha512-ePNZtj7pNIva/siZMg+HmbeozkIjqUIYdoymH8HaA3qK7LfzFN4WMBM8G6HQ9ZC+H3+Dnn5pqtiXpgLykaPOhw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@napi-rs/canvas-linux-arm-gnueabihf": { + "version": "0.1.100", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm-gnueabihf/-/canvas-linux-arm-gnueabihf-0.1.100.tgz", + "integrity": "sha512-d5cDB48oWFGU8/XPhUOFAlySgb/VAu7D+s8fi55K1Pcfg8aPplHWqMgibhVLU8ky7Pyg/fuiVLz4Nf3JrSTuUA==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@napi-rs/canvas-linux-arm64-gnu": { + "version": "0.1.100", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm64-gnu/-/canvas-linux-arm64-gnu-0.1.100.tgz", + "integrity": "sha512-rDxgxRu69RvDlX/bh9o22DxLsGr8EqsNgotL9+RwQE1S0b0cqeatqsw6aW45mukm0B42DIAaAacKaYQ8cqS1nw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@napi-rs/canvas-linux-arm64-musl": { + "version": "0.1.100", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm64-musl/-/canvas-linux-arm64-musl-0.1.100.tgz", + "integrity": "sha512-K3mDW66N+xT2/V439u1alFANiBUjdEx2gLiNYnCmUsva5jZMxWTjafBYwTzYK+EMFMHrUoabuU+T1BIP5CgbYQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@napi-rs/canvas-linux-riscv64-gnu": { + "version": "0.1.100", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-riscv64-gnu/-/canvas-linux-riscv64-gnu-0.1.100.tgz", + "integrity": "sha512-mooqUBTIsccZpnoQC4NgrC1v6C1vof39etLNMnBwCY+p0gajWJvAHLGQ6g/gGyS5YrpDW+GefSN4+Cvcr08UWw==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@napi-rs/canvas-linux-x64-gnu": { + "version": "0.1.100", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-x64-gnu/-/canvas-linux-x64-gnu-0.1.100.tgz", + "integrity": "sha512-1eCvkDCazm7FFhsT7DfGOdSaHgZVK3bt/dSBl5EWHOWmnz+I7j8tPseJqqD81NF+MH21jKUK4wQSDjN0mdhnTg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@napi-rs/canvas-linux-x64-musl": { + "version": "0.1.100", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-x64-musl/-/canvas-linux-x64-musl-0.1.100.tgz", + "integrity": "sha512-20arT6lnI19S68qNlii73TSEDbECNgzMz2EpldC1V3mZFuRkeujXkcebRk0LRJe9SEUAooYiLokfMViY8IX7yA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@napi-rs/canvas-win32-arm64-msvc": { + "version": "0.1.100", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-win32-arm64-msvc/-/canvas-win32-arm64-msvc-0.1.100.tgz", + "integrity": "sha512-DZFFT1wIAg37LJw37yhMRFfjATd3vTQzjZ1Yki8u2vhO6Hi5VE6BVaGQ1aaDu7xb4iMErz+9EOwjpS7xcxFeBw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@napi-rs/canvas-win32-x64-msvc": { + "version": "0.1.100", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-win32-x64-msvc/-/canvas-win32-x64-msvc-0.1.100.tgz", + "integrity": "sha512-MyT1j3mHC2+Lu4pBi9mKyMJhtP6U7k7EldY7sj/uS5gJA65gTXt8MefJQXLJo5d/vZbuWmfxzkEUNc/urV3pHA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@sindresorhus/merge-streams": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz", + "integrity": "sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "26.0.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-26.0.1.tgz", + "integrity": "sha512-fc3KiUoBt6kie0N9bIW3E47vZsuaMf0PM2AaUpLCLT0s/LvX1nxAim6Fc049cNxODPpGm6qRAuUOB86SkRuPQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~8.3.0" + } + }, + "node_modules/@webassemblyjs/ast": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", + "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/helper-numbers": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2" + } + }, + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", + "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", + "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", + "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", + "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.13.2", + "@webassemblyjs/helper-api-error": "1.13.2", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", + "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", + "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/wasm-gen": "1.14.1" + } + }, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", + "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@webassemblyjs/leb128": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", + "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/utf8": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", + "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", + "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/helper-wasm-section": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-opt": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1", + "@webassemblyjs/wast-printer": "1.14.1" + } + }, + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", + "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" + } + }, + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", + "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1" + } + }, + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", + "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-api-error": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" + } + }, + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", + "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webpack-cli/configtest": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-2.1.1.tgz", + "integrity": "sha512-wy0mglZpDSiSS0XHrVR+BAdId2+yxPSoJW8fsna3ZpYSlufjvxnP4YbKTCBZnNIcGN4r6ZPXV55X4mYExOfLmw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.15.0" + }, + "peerDependencies": { + "webpack": "5.x.x", + "webpack-cli": "5.x.x" + } + }, + "node_modules/@webpack-cli/info": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-2.0.2.tgz", + "integrity": "sha512-zLHQdI/Qs1UyT5UBdWNqsARasIA+AaF8t+4u2aS2nEpBQh2mWIVb8qAklq0eUENnC5mOItrIB4LiS9xMtph18A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.15.0" + }, + "peerDependencies": { + "webpack": "5.x.x", + "webpack-cli": "5.x.x" + } + }, + "node_modules/@webpack-cli/serve": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-2.0.5.tgz", + "integrity": "sha512-lqaoKnRYBdo1UgDX8uF24AfGMifWK19TxPmM5FHc2vAGxrJ/qtyUyFBWoY1tISZdelsQ5fBcOusifo5o5wSJxQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.15.0" + }, + "peerDependencies": { + "webpack": "5.x.x", + "webpack-cli": "5.x.x" + }, + "peerDependenciesMeta": { + "webpack-dev-server": { + "optional": true + } + } + }, + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/acorn": { + "version": "8.17.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.17.0.tgz", + "integrity": "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-import-phases": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz", + "integrity": "sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.13.0" + }, + "peerDependencies": { + "acorn": "^8.14.0" + } + }, + "node_modules/adler-32": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/adler-32/-/adler-32-1.3.1.tgz", + "integrity": "sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/ajv": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true, + "license": "MIT" + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "dev": true, + "license": "MIT" + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/autoprefixer": { + "version": "10.5.2", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.5.2.tgz", + "integrity": "sha512-rD5t5DwOjJdmSORcTq64j8MawTC+tbQ+HHqjR4NDumamy/ambn1UJrlKL+KdwujWxMkFjPM3pPHOEA9tl4767Q==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "browserslist": "^4.28.4", + "caniuse-lite": "^1.0.30001799", + "fraction.js": "^5.3.4", + "picocolors": "^1.1.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/babel-loader": { + "version": "9.2.1", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-9.2.1.tgz", + "integrity": "sha512-fqe8naHt46e0yIdkjUZYqddSXfej3AHajX+CSO5X7oy0EmPc6o5Xh+RClNoHjnieWz9AW4kZxW9yyFMhVB1QLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-cache-dir": "^4.0.0", + "schema-utils": "^4.0.0" + }, + "engines": { + "node": ">= 14.15.0" + }, + "peerDependencies": { + "@babel/core": "^7.12.0", + "webpack": ">=5" + } + }, + "node_modules/babel-plugin-polyfill-corejs2": { + "version": "0.4.17", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.17.tgz", + "integrity": "sha512-aTyf30K/rqAsNwN76zYrdtx8obu0E4KoUME29B1xj+B3WxgvWkp943vYQ+z8Mv3lw9xHXMHpvSPOBxzAkIa94w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.28.6", + "@babel/helper-define-polyfill-provider": "^0.6.8", + "semver": "^6.3.1" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.14.2.tgz", + "integrity": "sha512-coWpDLJ410R781Npmn/SIBZEsAetR4xVi0SxLMXPaMO4lSf1MwnkGYMtkFxew0Dn8B3/CpbpYxN0JCgg8mn67g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.6.8", + "core-js-compat": "^3.48.0" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-polyfill-regenerator": { + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.8.tgz", + "integrity": "sha512-M762rNHfSF1EV3SLtnCJXFoQbbIIz0OyRwnCmV0KPC7qosSfCO0QLTSuJX3ayAebubhE6oYBAYPrBA5ljowaZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.6.8" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.40", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.40.tgz", + "integrity": "sha512-BSSLZ9/Cjjv7Gtj5B68ZzXcXUg8iOf3fme+FCuh8rC/Go+Kmh8cox7M3A8dolou16s64QjLPOSdngh7GxXvkSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.28.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.4.tgz", + "integrity": "sha512-MTc8i/x9jBQd1iMw2CFGS+rwMa07eYjLR0CCTLDACl9xhxy+nIs3KeML/biicXtk9JrZ6dnnTatmc7ErPXIxqw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.10.38", + "caniuse-lite": "^1.0.30001799", + "electron-to-chromium": "^1.5.376", + "node-releases": "^2.0.48", + "update-browserslist-db": "^1.2.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001799", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001799.tgz", + "integrity": "sha512-hG1bReV+OUU+MOqK4t/ZWI0tZOyz3rqS9XuhOUz1cIcbwBKjOyJEJuw9ER5JuNyqxNk8u/JUVbGibBOL1yrjFw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/cfb": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cfb/-/cfb-1.2.2.tgz", + "integrity": "sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==", + "license": "Apache-2.0", + "dependencies": { + "adler-32": "~1.3.0", + "crc-32": "~1.2.0" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/chrome-trace-event": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", + "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0" + } + }, + "node_modules/clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/codepage": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/codepage/-/codepage-1.15.0.tgz", + "integrity": "sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/common-path-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/common-path-prefix/-/common-path-prefix-3.0.0.tgz", + "integrity": "sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==", + "dev": true, + "license": "ISC" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/copy-webpack-plugin": { + "version": "12.0.2", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-12.0.2.tgz", + "integrity": "sha512-SNwdBeHyII+rWvee/bTnAYyO8vfVdcSTud4EIb6jcZ8inLeWucJE0DnxXQBjlQ5zlteuuvooGQy3LIyGxhvlOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-glob": "^3.3.2", + "glob-parent": "^6.0.1", + "globby": "^14.0.0", + "normalize-path": "^3.0.0", + "schema-utils": "^4.2.0", + "serialize-javascript": "^6.0.2" + }, + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + } + }, + "node_modules/core-js-compat": { + "version": "3.49.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.49.0.tgz", + "integrity": "sha512-VQXt1jr9cBz03b331DFDCCP90b3fanciLkgiOoy8SBHy06gNf+vQ1A3WFLqG7I8TipYIKeYK9wxd0tUrvHcOZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "browserslist": "^4.28.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/cosmiconfig": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.2.tgz", + "integrity": "sha512-gtTZxTDau1wL7Y7zifc2dd8jHSK/k6BTx/2Xp/BpdlAdnlYWFVt7qhJqgwi7637yRwRQ3qL4ZidbB4I8tA5VOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.1", + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "license": "Apache-2.0", + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/css-loader": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-7.1.4.tgz", + "integrity": "sha512-vv3J9tlOl04WjiMvHQI/9tmIrCxVrj6PFbHemBB1iihpeRbi/I4h033eoFIhwxBBqLhI0KYFS7yvynBFhIZfTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "icss-utils": "^5.1.0", + "postcss": "^8.4.40", + "postcss-modules-extract-imports": "^3.1.0", + "postcss-modules-local-by-default": "^4.0.5", + "postcss-modules-scope": "^3.2.0", + "postcss-modules-values": "^4.0.0", + "postcss-value-parser": "^4.2.0", + "semver": "^7.6.3" + }, + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "@rspack/core": "0.x || ^1.0.0 || ^2.0.0-0", + "webpack": "^5.27.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "webpack": { + "optional": true + } + } + }, + "node_modules/css-loader/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "dev": true, + "license": "MIT" + }, + "node_modules/electron-to-chromium": { + "version": "1.5.380", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.380.tgz", + "integrity": "sha512-W6d5AbuEoRayO447cqrg6lKJIlscgRnnxOZl/08kfV71BQDoEBC7Wwis68z87LjyK6f4kWyTaubuDbhHKrZkbA==", + "dev": true, + "license": "ISC" + }, + "node_modules/enhanced-resolve": { + "version": "5.24.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.24.1.tgz", + "integrity": "sha512-7DdUaTjmNwMcH2gLr1qycesKII3BK4RLy/mdAb7x10Lq7bR4aNKHt1BR1ZALSv0rPM/hF5wYF0PhGop/rJm8vw==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.3.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/envinfo": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.21.0.tgz", + "integrity": "sha512-Lw7I8Zp5YKHFCXL7+Dz95g4CcbMEpgvqZNNq3AmlT5XAV6CgAAk6gyAMqn2zjw08K9BHfcNuKrMiCPLByGafow==", + "dev": true, + "license": "MIT", + "bin": { + "envinfo": "dist/cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/error-ex": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", + "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-module-lexer": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.1.0.tgz", + "integrity": "sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz", + "integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/fastest-levenshtein": { + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", + "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.9.1" + } + }, + "node_modules/fastq": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-cache-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-4.0.0.tgz", + "integrity": "sha512-9ZonPT4ZAK4a+1pUPVPZJapbi7O5qbbJPdYw/NOQWZZbVLdDTYM3A4R9z/DpAM08IDaFGsvPgiGZ82WEwUDWjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "common-path-prefix": "^3.0.0", + "pkg-dir": "^7.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/find-up": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-6.3.0.tgz", + "integrity": "sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^7.1.0", + "path-exists": "^5.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "license": "BSD-3-Clause", + "bin": { + "flat": "cli.js" + } + }, + "node_modules/frac": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/frac/-/frac-1.1.2.tgz", + "integrity": "sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/fraction.js": { + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz", + "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/rawify" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globby": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-14.1.0.tgz", + "integrity": "sha512-0Ia46fDOaT7k4og1PDW4YbodWWr3scS2vAr2lTbsplOt2WkKp0vQbkI9wKis/T5LV/dqPjO3bpS/z6GTJB82LA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sindresorhus/merge-streams": "^2.1.0", + "fast-glob": "^3.3.3", + "ignore": "^7.0.3", + "path-type": "^6.0.0", + "slash": "^5.1.0", + "unicorn-magic": "^0.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/hasown": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/icss-utils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", + "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-local": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", + "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-local/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/import-local/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/import-local/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-local/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/import-local/node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/import-local/node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/interpret": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz", + "integrity": "sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.16.2", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz", + "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "license": "MIT", + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/jiti": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz", + "integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz", + "integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/loader-runner": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.2.tgz", + "integrity": "sha512-DFEqQ3ihfS9blba08cLfYf1NRAIEm+dDjic073DRDc3/JspI/8wYmtDsHwd3+4hwvdxSK7PGaElfTmm0awWJ4w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.11.5" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/locate-path": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", + "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^6.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", + "dev": true, + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mini-css-extract-plugin": { + "version": "2.10.2", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.10.2.tgz", + "integrity": "sha512-AOSS0IdEB95ayVkxn5oGzNQwqAi2J0Jb/kKm43t7H73s8+f5873g0yuj0PNvK4dO75mu5DHg4nlgp4k6Kga8eg==", + "dev": true, + "license": "MIT", + "dependencies": { + "schema-utils": "^4.0.0", + "tapable": "^2.2.1" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/minimizer-webpack-plugin": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/minimizer-webpack-plugin/-/minimizer-webpack-plugin-5.6.1.tgz", + "integrity": "sha512-DoeAZz8Q1C1znwsUzej1fdoi4jCf7/+Em27ouLqfK/+3m8G+D7yDhUwrc3CNhjSzGUN1kn7Iv4sWmjflQHenpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.25", + "jest-worker": "^27.4.5", + "schema-utils": "^4.3.0", + "terser": "^5.31.1" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@minify-html/node": { + "optional": true + }, + "@swc/core": { + "optional": true + }, + "@swc/css": { + "optional": true + }, + "@swc/html": { + "optional": true + }, + "clean-css": { + "optional": true + }, + "cssnano": { + "optional": true + }, + "csso": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "html-minifier-terser": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "postcss": { + "optional": true + }, + "uglify-js": { + "optional": true + } + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.15", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.15.tgz", + "integrity": "sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.50", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.50.tgz", + "integrity": "sha512-J6l92tKHX6w8Jy5nO1Vuc01NoIiRGi/d6qBKVxh+IQ8Cr3b6HbVNfKiF8ZpFKufTwpwxMmce2W3iQZ861ZRyTg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/p-limit": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", + "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^1.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", + "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/papaparse": { + "version": "5.5.4", + "resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.5.4.tgz", + "integrity": "sha512-SwzWD9gl/ElwYLCI0nUja1mFJzjq2D8ziShfNBa7zCHzkOozeOGDwHWQ+tvCzEZcewecWZ5U7kUopDnG+DFYEQ==", + "license": "MIT" + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", + "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-type": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-6.0.0.tgz", + "integrity": "sha512-Vj7sf++t5pBD637NSfkxpHSMfWaeig5+DKWLhcqIYx6mWQz5hdJTGDVMQiJcw1ZYkhs7AazKDGpRVji1LJCZUQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pdfjs-dist": { + "version": "4.10.38", + "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-4.10.38.tgz", + "integrity": "sha512-/Y3fcFrXEAsMjJXeL9J8+ZG9U01LbuWaYypvDW2ycW1jL269L3js3DVBjDJ0Up9Np1uqDXsDrRihHANhZOlwdQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=20" + }, + "optionalDependencies": { + "@napi-rs/canvas": "^0.1.65" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-dir": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-7.0.0.tgz", + "integrity": "sha512-Ie9z/WINcxxLp27BKOCHGde4ITq9UklYKDzVo1nhk5sqGEXU3FpkwP5GM2voTGJkGd9B3Otl+Q4uwSOeSUtOBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^6.3.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/postcss": { + "version": "8.5.15", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", + "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.12", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-js": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.1.0.tgz", + "integrity": "sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/postcss-load-config": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz", + "integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "lilconfig": "^3.1.1" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "jiti": ">=1.21.0", + "postcss": ">=8.0.9", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + }, + "postcss": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/postcss-loader": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-8.2.1.tgz", + "integrity": "sha512-k98jtRzthjj3f76MYTs9JTpRqV1RaaMhEU0Lpw9OTmQZQdppg4B30VZ74BojuBHt3F4KyubHJoXCMUeM8Bqeow==", + "dev": true, + "license": "MIT", + "dependencies": { + "cosmiconfig": "^9.0.0", + "jiti": "^2.5.1", + "semver": "^7.6.2" + }, + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "@rspack/core": "0.x || ^1.0.0 || ^2.0.0-0", + "postcss": "^7.0.0 || ^8.0.1", + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "webpack": { + "optional": true + } + } + }, + "node_modules/postcss-loader/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/postcss-modules-extract-imports": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.1.0.tgz", + "integrity": "sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-local-by-default": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.2.0.tgz", + "integrity": "sha512-5kcJm/zk+GJDSfw+V/42fJ5fhjL5YbFDl8nVdXkJPLLW+Vf9mTD5Xe0wqIaDnLuL2U6cDNpTr+UQ+v2HWIBhzw==", + "dev": true, + "license": "MIT", + "dependencies": { + "icss-utils": "^5.0.0", + "postcss-selector-parser": "^7.0.0", + "postcss-value-parser": "^4.1.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-scope": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.2.1.tgz", + "integrity": "sha512-m9jZstCVaqGjTAuny8MdgE88scJnCiQSlSrOWcTQgM2t32UBe+MUmFSO5t7VMSfAf/FJKImAxBav8ooCHJXCJA==", + "dev": true, + "license": "ISC", + "dependencies": { + "postcss-selector-parser": "^7.0.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-values": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", + "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "icss-utils": "^5.0.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-nested": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", + "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^6.1.1" + }, + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-nested/node_modules/postcss-selector-parser": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.4.tgz", + "integrity": "sha512-bIoJLOmjCO1S9XdY/DcnR5hJxvrDir1PbGChrzXG3vw0/FOliy/fA3dmdhQ441kah4gKv+TwckGzex6wNS5cnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-selector-parser": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.4.tgz", + "integrity": "sha512-HeP7D2wyhkR+XaK6v4W8oRF62Dsz4flyuczALJp61GckGm42u1saSSJ/0auvcBqxs3jMRFEcPK34At/0JBKdOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/rechoir": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz", + "integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve": "^1.20.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", + "dev": true, + "license": "MIT" + }, + "node_modules/regenerate-unicode-properties": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.2.tgz", + "integrity": "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "regenerate": "^1.4.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regexpu-core": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.4.0.tgz", + "integrity": "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA==", + "dev": true, + "license": "MIT", + "dependencies": { + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.2.2", + "regjsgen": "^0.8.0", + "regjsparser": "^0.13.0", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.2.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regjsgen": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz", + "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/regjsparser": { + "version": "0.13.2", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.13.2.tgz", + "integrity": "sha512-NgRBy2Nx/bE+9F27nVHnqcN5HjyLmecqsqx2PJHu3/IEtADD4WuxuXIVExD5PoSDFVrl78dOonfcOe5O+5nbzQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "jsesc": "~3.1.0" + }, + "bin": { + "regjsparser": "bin/parser" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.12", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", + "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-cwd/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/schema-utils": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", + "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "dev": true, + "license": "MIT", + "dependencies": { + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/slash": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", + "integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/ssf": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/ssf/-/ssf-0.11.2.tgz", + "integrity": "sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==", + "license": "Apache-2.0", + "dependencies": { + "frac": "~1.1.2" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/sucrase": { + "version": "3.35.1", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz", + "integrity": "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "tinyglobby": "^0.2.11", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tailwindcss": { + "version": "3.4.19", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.19.tgz", + "integrity": "sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.6.0", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.3.2", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.21.7", + "lilconfig": "^3.1.3", + "micromatch": "^4.0.8", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.1.1", + "postcss": "^8.4.47", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.2 || ^5.0 || ^6.0", + "postcss-nested": "^6.2.0", + "postcss-selector-parser": "^6.1.2", + "resolve": "^1.22.8", + "sucrase": "^3.35.0" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tailwindcss/node_modules/jiti": { + "version": "1.21.7", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", + "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "bin/jiti.js" + } + }, + "node_modules/tailwindcss/node_modules/postcss-selector-parser": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.4.tgz", + "integrity": "sha512-bIoJLOmjCO1S9XdY/DcnR5hJxvrDir1PbGChrzXG3vw0/FOliy/fA3dmdhQ441kah4gKv+TwckGzex6wNS5cnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/tapable": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", + "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/terser": { + "version": "5.48.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.48.0.tgz", + "integrity": "sha512-J/9An6vs9Us6wKRriSFXBWdRZapREHqFzdNUKk0pmu804EMR6dr6winwo7e5JDxN4xahxQsuysyYFwlwj4XN/Q==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.15.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/undici-types": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.3.0.tgz", + "integrity": "sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/unicode-canonical-property-names-ecmascript": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", + "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", + "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-value-ecmascript": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.1.tgz", + "integrity": "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-property-aliases-ecmascript": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.2.0.tgz", + "integrity": "sha512-hpbDzxUY9BFwX+UeBnxv3Sh1q7HFxj48DTmXchNgRa46lO8uj3/1iEn3MiNUYTg1g9ctIqXCCERn8gYZhHC5lQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicorn-magic": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz", + "integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/watchpack": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.5.2.tgz", + "integrity": "sha512-6i/00NBjP4yGPs+caKSyRfpTF/8Torsu0MOW3mMzIbhgISFder8i7xbqgHlLMwJrdiN8ndBV3UA1/AfzPSr+jg==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack": { + "version": "5.108.1", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.108.1.tgz", + "integrity": "sha512-UUCihHQK3O7483Woa0SulNLDeAiOhHI2PN2PAPU4fVWJqbzhv04EJ8FaWtB9WWh3i8fRt28543U7VfuJTOrpgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.8", + "@types/json-schema": "^7.0.15", + "@webassemblyjs/ast": "^1.14.1", + "@webassemblyjs/wasm-edit": "^1.14.1", + "@webassemblyjs/wasm-parser": "^1.14.1", + "acorn": "^8.16.0", + "acorn-import-phases": "^1.0.3", + "browserslist": "^4.28.1", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.22.2", + "es-module-lexer": "^2.1.0", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "graceful-fs": "^4.2.11", + "loader-runner": "^4.3.2", + "mime-db": "^1.54.0", + "minimizer-webpack-plugin": "^5.6.1", + "neo-async": "^2.6.2", + "schema-utils": "^4.3.3", + "tapable": "^2.3.0", + "watchpack": "^2.5.2", + "webpack-sources": "^3.5.0" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-cli": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-5.1.4.tgz", + "integrity": "sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@discoveryjs/json-ext": "^0.5.0", + "@webpack-cli/configtest": "^2.1.1", + "@webpack-cli/info": "^2.0.2", + "@webpack-cli/serve": "^2.0.5", + "colorette": "^2.0.14", + "commander": "^10.0.1", + "cross-spawn": "^7.0.3", + "envinfo": "^7.7.3", + "fastest-levenshtein": "^1.0.12", + "import-local": "^3.0.2", + "interpret": "^3.1.1", + "rechoir": "^0.8.0", + "webpack-merge": "^5.7.3" + }, + "bin": { + "webpack-cli": "bin/cli.js" + }, + "engines": { + "node": ">=14.15.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "5.x.x" + }, + "peerDependenciesMeta": { + "@webpack-cli/generators": { + "optional": true + }, + "webpack-bundle-analyzer": { + "optional": true + }, + "webpack-dev-server": { + "optional": true + } + } + }, + "node_modules/webpack-cli/node_modules/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/webpack-merge": { + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.10.0.tgz", + "integrity": "sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "clone-deep": "^4.0.1", + "flat": "^5.0.2", + "wildcard": "^2.0.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/webpack-sources": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.5.0.tgz", + "integrity": "sha512-HPuy+uuoTCaaoEoI1LQ3JN9+vrPBvEesnnX1jADHy728cHSMlq4wUc4afYqahq2B1mhQVZxCXOkNTnXltr+2vQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wildcard": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz", + "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/wmf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wmf/-/wmf-1.0.2.tgz", + "integrity": "sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/word": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/word/-/word-0.3.0.tgz", + "integrity": "sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/xlsx": { + "version": "0.18.5", + "resolved": "https://registry.npmjs.org/xlsx/-/xlsx-0.18.5.tgz", + "integrity": "sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==", + "license": "Apache-2.0", + "dependencies": { + "adler-32": "~1.3.0", + "cfb": "~1.2.1", + "codepage": "~1.15.0", + "crc-32": "~1.2.1", + "ssf": "~0.11.2", + "wmf": "~1.0.1", + "word": "~0.3.0" + }, + "bin": { + "xlsx": "bin/xlsx.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yocto-queue": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.2.tgz", + "integrity": "sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/agrifine-extension/package.json b/agrifine-extension/package.json new file mode 100644 index 0000000000..a33d9c5e86 --- /dev/null +++ b/agrifine-extension/package.json @@ -0,0 +1,30 @@ +{ + "name": "agrifine-extension", + "version": "0.1.0", + "description": "Browser extension farm data dashboard", + "private": true, + "scripts": { + "build": "webpack --config webpack/webpack.config.js", + "watch": "webpack --config webpack/webpack.config.js --watch", + "build:prod": "NODE_ENV=production webpack --config webpack/webpack.config.js" + }, + "devDependencies": { + "@babel/core": "^7.24.0", + "@babel/preset-env": "^7.24.0", + "autoprefixer": "^10.4.19", + "babel-loader": "^9.1.3", + "copy-webpack-plugin": "^12.0.2", + "css-loader": "^7.1.2", + "mini-css-extract-plugin": "^2.9.0", + "postcss": "^8.4.38", + "postcss-loader": "^8.1.1", + "tailwindcss": "^3.4.3", + "webpack": "^5.91.0", + "webpack-cli": "^5.1.4" + }, + "dependencies": { + "papaparse": "^5.4.1", + "pdfjs-dist": "^4.2.67", + "xlsx": "^0.18.5" + } +} diff --git a/agrifine-extension/postcss.config.js b/agrifine-extension/postcss.config.js new file mode 100644 index 0000000000..12a703d900 --- /dev/null +++ b/agrifine-extension/postcss.config.js @@ -0,0 +1,6 @@ +module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +}; diff --git a/agrifine-extension/public/icons/icon128.png b/agrifine-extension/public/icons/icon128.png new file mode 100644 index 0000000000000000000000000000000000000000..f05b9b66c0608aa2680d833127f8c8b61228276b GIT binary patch literal 306 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H1SEZ8zRdwrKRsO>Ln`LHz39lxpultBz)T0e zdlUM4Y^yw3O?Fzo&)OJi-@(q{V9mgAi;>|1A45YK1H%?(h6S<=3~w12GKQ{{-7}?T VUW1NgMPMK>c)I$ztaD0e0ssl)OE~}l literal 0 HcmV?d00001 diff --git a/agrifine-extension/public/icons/icon16.png b/agrifine-extension/public/icons/icon16.png new file mode 100644 index 0000000000000000000000000000000000000000..61470ed02ac77f35ea53559dab5838901dc9941e GIT binary patch literal 79 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61SBU+%rFB|VxBIJAr*6y1#&m2avJtzNnACk bF=b$=y?=>uo!+7^Kotz0u6{1-oD!M=1= literal 0 HcmV?d00001 diff --git a/agrifine-extension/public/icons/icon32.png b/agrifine-extension/public/icons/icon32.png new file mode 100644 index 0000000000000000000000000000000000000000..3be3c78b46871f3c34d56bd7c13de60620357f36 GIT binary patch literal 99 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1SJ1Ryj={Wv^-rLLn`JZ3*>HCs+lCQHQ`EA iAh(+?I-{+Nl|iiAL+|MMv)h5X7(8A5T-G@yGywp+VH~9Z literal 0 HcmV?d00001 diff --git a/agrifine-extension/public/icons/icon48.png b/agrifine-extension/public/icons/icon48.png new file mode 100644 index 0000000000000000000000000000000000000000..61d2f0fd5c01f419bb630a3d13dbb3ae213672ff GIT binary patch literal 123 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1SD@HmdKI;Vst00ed?H~;_u literal 0 HcmV?d00001 diff --git a/agrifine-extension/src/background/index.js b/agrifine-extension/src/background/index.js new file mode 100644 index 0000000000..76e83d262e --- /dev/null +++ b/agrifine-extension/src/background/index.js @@ -0,0 +1,42 @@ +import { sessionGet, sessionSet, KEYS } from '../utils/storage.js'; +import { fetchAnthropic } from '../utils/api.js'; + +// Open the side panel when the action icon is clicked +chrome.sidePanel.setPanelBehavior({ openPanelOnActionClick: true }).catch(console.error); + +// ── Message router ──────────────────────────────────────────────────────────── +chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => { + switch (message.type) { + case 'ANTHROPIC_REQUEST': + handleAnthropicRequest(message.payload).then(sendResponse).catch((err) => + sendResponse({ error: err.message }) + ); + return true; // keep channel open for async response + + case 'SET_API_KEY': + sessionSet(KEYS.API_KEY, message.payload.key) + .then(() => sendResponse({ ok: true })) + .catch((err) => sendResponse({ error: err.message })); + return true; + + case 'GET_PAGE_CONTENT': + // Content script relays page text; background stores it temporarily + sendResponse({ ok: true }); + return false; + + default: + return false; + } +}); + +async function handleAnthropicRequest({ system, userMessage, maxTokens }) { + const text = await fetchAnthropic({ system, userMessage, maxTokens }); + return { text }; +} + +// Keep service worker alive during active side-panel sessions +chrome.runtime.onConnect.addListener((port) => { + if (port.name === 'keepalive') { + port.onDisconnect.addListener(() => {}); + } +}); diff --git a/agrifine-extension/src/content/index.js b/agrifine-extension/src/content/index.js new file mode 100644 index 0000000000..a9fb43b54f --- /dev/null +++ b/agrifine-extension/src/content/index.js @@ -0,0 +1,25 @@ +// Content script — minimal surface. Relays page metadata to background on request. + +chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => { + if (message.type === 'GET_PAGE_INFO') { + sendResponse({ + url: window.location.href, + title: document.title, + text: extractMainText(), + }); + } +}); + +function extractMainText() { + const selectors = ['article', 'main', '[role="main"]', '.content', '#content', 'body']; + for (const sel of selectors) { + const el = document.querySelector(sel); + if (el) { + // Strip scripts and styles, return first 8000 chars + const clone = el.cloneNode(true); + clone.querySelectorAll('script,style,nav,header,footer,aside').forEach((n) => n.remove()); + return clone.innerText.trim().slice(0, 8000); + } + } + return document.body?.innerText?.slice(0, 8000) ?? ''; +} diff --git a/agrifine-extension/src/modules/carbon-estimator/index.js b/agrifine-extension/src/modules/carbon-estimator/index.js new file mode 100644 index 0000000000..097e456a76 --- /dev/null +++ b/agrifine-extension/src/modules/carbon-estimator/index.js @@ -0,0 +1,56 @@ +// Carbon Estimator — Phase 7 stub +// Full implementation: soil organic matter, Scope 3 emissions, USDA NRCS eFOTG API, PDF export + +export function CarbonEstimatorModule() { + return { + id: 'carbon-estimator', + label: 'Carbon', + + render(container) { + container.innerHTML = ` +
Carbon Estimator
+ +
+ +
+
+
🌿
+
+

Carbon Estimator

+ Coming in Phase 7 +
+
+

+ The Carbon Estimator will calculate your operation's Scope 3 emissions profile + and estimate carbon sequestration potential per field using USDA emission factors. +

+
+ + +
+ ${[ + ['📊', 'Scope 3 Emissions Profile', 'Based on fuel use, crop type, and animal operations'], + ['🌱', 'Sequestration Potential', 'Per-field estimate using soil type and land cover'], + ['🏛️', 'USDA Program Matcher', 'Match your practices to EQIP, CSP, and CRP programs'], + ['📄', 'Carbon Credit PDF', 'Downloadable eligibility summary for carbon marketplaces'], + ['📡', 'Marketplace Handoff', 'Send your credit profile to Nori, Pachama, or others (Phase 8)'], + ].map(([icon, title, desc]) => ` +
+ ${icon} +
+

${title}

+

${desc}

+
+
`).join('')} +
+ + +
+

Your field profile data is already being collected.

+

Carbon estimates will populate automatically when Phase 7 lands.

+
+
+ `; + }, + }; +} diff --git a/agrifine-extension/src/modules/dashboard/index.js b/agrifine-extension/src/modules/dashboard/index.js new file mode 100644 index 0000000000..390d7db1b3 --- /dev/null +++ b/agrifine-extension/src/modules/dashboard/index.js @@ -0,0 +1,183 @@ +import { + getReadingList, getIngestedFiles, getFieldProfiles, buildContextBundle, +} from '../../utils/storage.js'; +import { callAnthropic } from '../../utils/api.js'; + +const CATEGORIES = ['all', 'land', 'equipment', 'harvest', 'finance', 'carbon', 'weather']; + +function tagCategory(item) { + const tags = item.tags ?? []; + const data = JSON.stringify(item.structuredData ?? {}).toLowerCase(); + if (tags.includes('land') || data.includes('field') || data.includes('acre')) return 'land'; + if (tags.includes('equipment') || data.includes('equipment')) return 'equipment'; + if (data.includes('harvest') || data.includes('yield')) return 'harvest'; + if (tags.includes('finance') || data.includes('financ') || data.includes('expense')) return 'finance'; + if (tags.includes('carbon') || data.includes('carbon')) return 'carbon'; + if (tags.includes('weather') || data.includes('weather')) return 'weather'; + return 'other'; +} + +export function DashboardModule() { + let activeCategory = 'all'; + let keyword = ''; + let aiAnswer = ''; + let aiLoading = false; + + return { + id: 'dashboard', + label: 'Dashboard', + + async render(container) { + container.innerHTML = ` +
Farm Dashboard
+ + +
+
+ + +
+ +
+ + +
+
+ ${CATEGORIES.map((c) => ` + `).join('')} +
+ +
+ + +
+ `; + + this._bindEvents(container); + await this._renderDashboard(container); + }, + + _bindEvents(container) { + container.querySelectorAll('.cat-btn').forEach((btn) => { + btn.addEventListener('click', async () => { + activeCategory = btn.dataset.cat; + container.querySelectorAll('.cat-btn').forEach((b) => { + b.className = `cat-btn text-xs px-2.5 py-1 rounded-full border transition border-gray-300 text-gray-600 hover:border-agri-400`; + }); + btn.className = `cat-btn text-xs px-2.5 py-1 rounded-full border transition bg-agri-600 text-white border-agri-600`; + await this._renderDashboard(container); + }); + }); + + container.querySelector('#dash-search').addEventListener('input', async (e) => { + keyword = e.target.value.toLowerCase(); + await this._renderDashboard(container); + }); + + container.querySelector('#dash-ai-btn').addEventListener('click', () => this._runAIQuery(container)); + container.querySelector('#dash-ai-input').addEventListener('keydown', (e) => { + if (e.key === 'Enter') this._runAIQuery(container); + }); + }, + + async _runAIQuery(container) { + if (aiLoading) return; + const input = container.querySelector('#dash-ai-input'); + const question = input.value.trim(); + if (!question) return; + + aiLoading = true; + const answerEl = container.querySelector('#dash-ai-answer'); + answerEl.classList.remove('hidden'); + answerEl.innerHTML = ' Thinking…'; + + try { + const contextBundle = await buildContextBundle(); + const [readingList, ingestedFiles, fieldProfiles] = await Promise.all([ + getReadingList(), getIngestedFiles(), getFieldProfiles(), + ]); + + const dataContext = [ + contextBundle, + '', + 'INGESTED FILES:', + ingestedFiles.map((f) => `${f.filename}: ${JSON.stringify(f.structuredData ?? {}).slice(0, 400)}`).join('\n') || '(none)', + ].join('\n'); + + const answer = await callAnthropic({ + system: `You are a farm management AI assistant with access to this farm's data:\n\n${dataContext}`, + userMessage: question, + maxTokens: 512, + }); + + answerEl.innerHTML = `

Answer

${answer}`; + } catch (err) { + answerEl.textContent = `Error: ${err.message}`; + } finally { + aiLoading = false; + } + }, + + async _renderDashboard(container) { + const [readingList, ingestedFiles, fieldProfiles] = await Promise.all([ + getReadingList(), getIngestedFiles(), getFieldProfiles(), + ]); + + const allItems = [ + ...readingList.map((i) => ({ ...i, _source: 'reading', _category: tagCategory(i) })), + ...ingestedFiles.map((f) => ({ ...f, _source: 'file', _category: tagCategory(f) })), + ...fieldProfiles.map((p) => ({ + ...p, _source: 'field', _category: 'land', tags: ['land'], + title: `Field: ${p.name}`, summary: `${p.acres ?? '?'} ac — ${p.soilType ?? 'unknown soil'}`, + })), + ]; + + const filtered = allItems.filter((item) => { + const catMatch = activeCategory === 'all' || item._category === activeCategory; + const kwMatch = !keyword || + (item.title ?? '').toLowerCase().includes(keyword) || + (item.summary ?? '').toLowerCase().includes(keyword) || + (item.filename ?? '').toLowerCase().includes(keyword) || + (item.name ?? '').toLowerCase().includes(keyword); + return catMatch && kwMatch; + }); + + const listEl = container.querySelector('#dash-list'); + + if (filtered.length === 0) { + listEl.innerHTML = `

No data matches your filters.

`; + return; + } + + listEl.innerHTML = filtered.map((item) => { + const sourceIcon = { reading: '📖', file: '📄', field: '🌱' }[item._source] ?? '•'; + const title = item.title ?? item.filename ?? item.name ?? 'Untitled'; + const sub = item.summary ?? item.preview?.slice(0, 120) ?? ''; + const date = item.savedAt ?? item.uploadedAt ?? item.createdAt ?? ''; + return ` +
+
+ ${sourceIcon} +
+

${title}

+ ${sub ? `

${sub}

` : ''} +
+ ${item._category} + ${(item.tags ?? []).filter((t) => t !== item._category).slice(0, 2).map((t) => `${t}`).join('')} + ${date ? `${new Date(date).toLocaleDateString()}` : ''} +
+
+
+
`; + }).join(''); + }, + }; +} diff --git a/agrifine-extension/src/modules/data-ingest/index.js b/agrifine-extension/src/modules/data-ingest/index.js new file mode 100644 index 0000000000..dce5edbc3b --- /dev/null +++ b/agrifine-extension/src/modules/data-ingest/index.js @@ -0,0 +1,234 @@ +import { getIngestedFiles, saveIngestedFile, deleteIngestedFile } from '../../utils/storage.js'; +import { callAnthropic } from '../../utils/api.js'; + +const SUPPORTED_TYPES = { + 'text/csv': 'CSV', + 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': 'Excel', + 'application/vnd.ms-excel': 'Excel', + 'application/pdf': 'PDF', +}; + +export function DataIngestModule() { + return { + id: 'data-ingest', + label: 'Data Ingest', + + async render(container) { + container.innerHTML = ` +
Data Ingest
+ + +
+
+ + + +

Drop CSV, Excel, or PDF here

+

or click to select a file

+ +
+
+
+ + +
+ `; + + this._bindEvents(container); + await this._renderFileList(container); + }, + + _bindEvents(container) { + const dropZone = container.querySelector('#drop-zone'); + const fileInput = container.querySelector('#file-input'); + + dropZone.addEventListener('click', () => fileInput.click()); + + dropZone.addEventListener('dragover', (e) => { + e.preventDefault(); + dropZone.classList.add('border-agri-600', 'bg-agri-50'); + }); + + dropZone.addEventListener('dragleave', () => { + dropZone.classList.remove('border-agri-600', 'bg-agri-50'); + }); + + dropZone.addEventListener('drop', (e) => { + e.preventDefault(); + dropZone.classList.remove('border-agri-600', 'bg-agri-50'); + const file = e.dataTransfer.files[0]; + if (file) this._processFile(file, container); + }); + + fileInput.addEventListener('change', () => { + if (fileInput.files[0]) this._processFile(fileInput.files[0], container); + }); + }, + + async _processFile(file, container) { + const status = container.querySelector('#ingest-status'); + const typeName = SUPPORTED_TYPES[file.type] ?? (file.name.endsWith('.csv') ? 'CSV' : null); + + if (!typeName) { + status.textContent = 'Unsupported file type.'; + return; + } + + status.textContent = `Parsing ${typeName}…`; + let extractedText = ''; + + try { + if (typeName === 'CSV') { + extractedText = await this._parseCSV(file); + } else if (typeName === 'Excel') { + extractedText = await this._parseExcel(file); + } else if (typeName === 'PDF') { + extractedText = await this._parsePDF(file); + } + } catch (err) { + status.textContent = `Parse error: ${err.message}`; + return; + } + + status.textContent = 'Extracting structured data with AI…'; + let structuredData = null; + + try { + const raw = await callAnthropic({ + system: 'You are an agricultural data analyst. Extract and return structured JSON from this document. Identify: operation type, field names, dates, quantities, equipment, crop types, financial figures, and any carbon or emissions data. Return only valid JSON.', + userMessage: extractedText.slice(0, 6000), + maxTokens: 1024, + }); + structuredData = JSON.parse(raw); + } catch (_) { + structuredData = { raw_preview: extractedText.slice(0, 500), parse_error: 'AI extraction unavailable' }; + } + + const record = { + id: `file_${Date.now()}`, + filename: file.name, + type: typeName, + uploadedAt: new Date().toISOString(), + structuredData, + preview: Object.entries(structuredData ?? {}) + .filter(([k]) => k !== 'raw_preview') + .slice(0, 5) + .map(([k, v]) => `${k}: ${JSON.stringify(v).slice(0, 80)}`) + .join('\n'), + }; + + await saveIngestedFile(record); + status.textContent = 'File processed!'; + setTimeout(() => { status.textContent = ''; }, 2000); + await this._renderFileList(container); + }, + + _parseCSV(file) { + return new Promise((resolve, reject) => { + // PapaParse is loaded dynamically to keep the background bundle lean + import('papaparse').then(({ default: Papa }) => { + Papa.parse(file, { + complete: (results) => { + const rows = results.data.slice(0, 200); + resolve(rows.map((r) => r.join(',')).join('\n')); + }, + error: reject, + }); + }); + }); + }, + + _parseExcel(file) { + return new Promise((resolve, reject) => { + const reader = new FileReader(); + reader.onload = async (e) => { + try { + const { read, utils } = await import('xlsx'); + const wb = read(e.target.result, { type: 'array' }); + const lines = []; + wb.SheetNames.slice(0, 3).forEach((name) => { + const ws = wb.Sheets[name]; + lines.push(`Sheet: ${name}`); + lines.push(utils.sheet_to_csv(ws).split('\n').slice(0, 100).join('\n')); + }); + resolve(lines.join('\n')); + } catch (err) { + reject(err); + } + }; + reader.onerror = reject; + reader.readAsArrayBuffer(file); + }); + }, + + _parsePDF(file) { + return new Promise((resolve, reject) => { + const reader = new FileReader(); + reader.onload = async (e) => { + try { + const pdfjsLib = await import('pdfjs-dist'); + pdfjsLib.GlobalWorkerOptions.workerSrc = chrome.runtime.getURL('pdf.worker.js'); + const pdf = await pdfjsLib.getDocument({ data: e.target.result }).promise; + const pages = Math.min(pdf.numPages, 10); + const texts = []; + for (let i = 1; i <= pages; i++) { + const page = await pdf.getPage(i); + const content = await page.getTextContent(); + texts.push(content.items.map((s) => s.str).join(' ')); + } + resolve(texts.join('\n')); + } catch (err) { + reject(err); + } + }; + reader.onerror = reject; + reader.readAsArrayBuffer(file); + }); + }, + + async _renderFileList(container) { + const files = await getIngestedFiles(); + const listEl = container.querySelector('#file-list'); + + if (files.length === 0) { + listEl.innerHTML = ` +
+ + + +

No files ingested yet.

+

Upload a CSV, Excel, or PDF file above.

+
`; + return; + } + + listEl.innerHTML = files.map((f) => ` +
+
+
+ ${f.type} +

${f.filename}

+

${new Date(f.uploadedAt).toLocaleDateString()}

+
+ +
+ ${f.preview ? `
${f.preview}
` : ''} +
+ `).join(''); + + listEl.querySelectorAll('.file-delete-btn').forEach((btn) => { + btn.addEventListener('click', async () => { + await deleteIngestedFile(btn.dataset.id); + await this._renderFileList(container); + }); + }); + }, + }; +} diff --git a/agrifine-extension/src/modules/field-profile/index.js b/agrifine-extension/src/modules/field-profile/index.js new file mode 100644 index 0000000000..646f650880 --- /dev/null +++ b/agrifine-extension/src/modules/field-profile/index.js @@ -0,0 +1,179 @@ +import { getFieldProfiles, saveFieldProfile, deleteFieldProfile } from '../../utils/storage.js'; + +export function FieldProfileModule() { + let showForm = false; + let expandedId = null; + + return { + id: 'field-profile', + label: 'Field Profiles', + + async render(container) { + container.innerHTML = ` +
Field Profiles
+ +
+ +
+ + + + + +
+ `; + + this._bindEvents(container); + await this._renderList(container); + }, + + _bindEvents(container) { + container.querySelector('#fp-new-btn').addEventListener('click', () => { + showForm = !showForm; + container.querySelector('#fp-form').classList.toggle('hidden', !showForm); + }); + + container.querySelector('#fp-cancel-btn').addEventListener('click', () => { + showForm = false; + container.querySelector('#fp-form').classList.add('hidden'); + }); + + container.querySelector('#fp-save-btn').addEventListener('click', async () => { + const name = container.querySelector('#fp-name').value.trim(); + if (!name) return; + + const profile = { + id: `fp_${Date.now()}`, + name, + cluId: container.querySelector('#fp-clu').value.trim() || null, + acres: parseFloat(container.querySelector('#fp-acres').value) || null, + soilType: container.querySelector('#fp-soil').value.trim() || null, + coordinates: { + lat: parseFloat(container.querySelector('#fp-lat').value) || null, + lon: parseFloat(container.querySelector('#fp-lon').value) || null, + }, + notes: container.querySelector('#fp-notes').value.trim() || null, + cropHistory: [], // populated from ingested data in Phase 3 + harvestRecords: [], // populated from ingested CSVs in Phase 3 + weatherData: null, // Phase 6 + carbonPotential: null, // Phase 7 + createdAt: new Date().toISOString(), + }; + + await saveFieldProfile(profile); + showForm = false; + container.querySelector('#fp-form').classList.add('hidden'); + await this._renderList(container); + }); + }, + + async _renderList(container) { + const profiles = await getFieldProfiles(); + const listEl = container.querySelector('#fp-list'); + + if (profiles.length === 0) { + listEl.innerHTML = ` +
+ + + +

No field profiles yet.

+

Create a profile for each field in your operation.

+
`; + return; + } + + listEl.innerHTML = profiles.map((p) => ` +
+
+
+

${p.name}

+
+ ${p.acres ? `${p.acres} ac` : ''} + ${p.soilType ? `${p.soilType}` : ''} + ${p.cluId ? `CLU ${p.cluId}` : ''} +
+
+
+ + + + +
+
+ + +
+ ${p.coordinates?.lat ? `

📍 ${p.coordinates.lat.toFixed(4)}, ${p.coordinates.lon.toFixed(4)}

` : ''} + ${p.notes ? `

📝 ${p.notes}

` : ''} +

Weather data: Phase 6

+

Carbon potential: Phase 7

+

Added ${new Date(p.createdAt).toLocaleDateString()}

+
+
+ `).join(''); + + listEl.querySelectorAll('.agri-card').forEach((card) => { + card.addEventListener('click', async (e) => { + if (e.target.closest('.fp-delete-btn')) return; + const id = card.dataset.id; + expandedId = expandedId === id ? null : id; + await this._renderList(container); + }); + }); + + listEl.querySelectorAll('.fp-delete-btn').forEach((btn) => { + btn.addEventListener('click', async (e) => { + e.stopPropagation(); + await deleteFieldProfile(btn.dataset.id); + if (expandedId === btn.dataset.id) expandedId = null; + await this._renderList(container); + }); + }); + }, + }; +} diff --git a/agrifine-extension/src/modules/reading-list/index.js b/agrifine-extension/src/modules/reading-list/index.js new file mode 100644 index 0000000000..8c3db7c100 --- /dev/null +++ b/agrifine-extension/src/modules/reading-list/index.js @@ -0,0 +1,154 @@ +import { getReadingList, saveReadingItem, deleteReadingItem } from '../../utils/storage.js'; +import { callAnthropic, AGRICULTURE_TAGS } from '../../utils/api.js'; + +export function ReadingListModule() { + let currentTag = 'all'; + + return { + id: 'reading-list', + label: 'Reading List', + + async render(container) { + container.innerHTML = ` +
Reading List
+ + +
+ +
+
+ + +
+ + ${AGRICULTURE_TAGS.map((t) => ``).join('')} +
+ + +
+ `; + + this._bindEvents(container); + await this._renderList(container); + }, + + _bindEvents(container) { + container.querySelector('#rl-save-btn').addEventListener('click', () => this._savePage(container)); + + container.querySelectorAll('.tag-filter-btn').forEach((btn) => { + btn.addEventListener('click', async () => { + currentTag = btn.dataset.tag; + container.querySelectorAll('.tag-filter-btn').forEach((b) => { + b.classList.remove('bg-agri-600', 'text-white'); + b.classList.add('bg-agri-100', 'text-agri-800'); + }); + btn.classList.add('bg-agri-600', 'text-white'); + btn.classList.remove('bg-agri-100', 'text-agri-800'); + await this._renderList(container); + }); + }); + }, + + async _savePage(container) { + const status = container.querySelector('#rl-save-status'); + status.textContent = 'Fetching page info…'; + + const [tab] = await chrome.tabs.query({ active: true, currentWindow: true }); + if (!tab) { + status.textContent = 'No active tab found.'; + return; + } + + let pageText = ''; + try { + const resp = await chrome.tabs.sendMessage(tab.id, { type: 'GET_PAGE_INFO' }); + pageText = resp?.text ?? ''; + } catch (_) { + pageText = ''; + } + + status.textContent = 'Summarising with AI…'; + let summary = ''; + let tags = []; + + try { + const rawResponse = await callAnthropic({ + system: 'You are an agricultural research assistant. Given web page text, return a JSON object with two fields: "summary" (2-3 sentence plain English summary focused on agricultural relevance) and "tags" (array of relevant tags from: agriculture, equipment, land, carbon, USDA, dairy, finance, weather). Return only valid JSON.', + userMessage: `Title: ${tab.title}\nURL: ${tab.url}\n\nContent:\n${pageText.slice(0, 4000)}`, + maxTokens: 256, + }); + + const parsed = JSON.parse(rawResponse); + summary = parsed.summary ?? ''; + tags = parsed.tags ?? []; + } catch (_) { + summary = '(AI summary unavailable)'; + tags = ['agriculture']; + } + + const item = { + id: `rl_${Date.now()}`, + url: tab.url, + title: tab.title, + savedAt: new Date().toISOString(), + summary, + tags, + }; + + await saveReadingItem(item); + status.textContent = 'Saved!'; + setTimeout(() => { status.textContent = ''; }, 2000); + await this._renderList(container); + }, + + async _renderList(container) { + const list = await getReadingList(); + const filtered = currentTag === 'all' ? list : list.filter((i) => i.tags?.includes(currentTag)); + const listEl = container.querySelector('#rl-list'); + + if (filtered.length === 0) { + listEl.innerHTML = ` +
+ + + +

No saved pages yet.

+

Browse to a page and click "Save current page".

+
`; + return; + } + + listEl.innerHTML = filtered.map((item) => ` +
+
+ ${item.title} + +
+ ${item.summary ? `

${item.summary}

` : ''} +
+ ${(item.tags ?? []).map((t) => `${t}`).join('')} +
+

${new Date(item.savedAt).toLocaleDateString()}

+
+ `).join(''); + + listEl.querySelectorAll('.rl-delete-btn').forEach((btn) => { + btn.addEventListener('click', async () => { + await deleteReadingItem(btn.dataset.id); + await this._renderList(container); + }); + }); + }, + }; +} diff --git a/agrifine-extension/src/sidebar/index.js b/agrifine-extension/src/sidebar/index.js new file mode 100644 index 0000000000..72d1209ea9 --- /dev/null +++ b/agrifine-extension/src/sidebar/index.js @@ -0,0 +1,90 @@ +import './sidebar.css'; +import { ReadingListModule } from '../modules/reading-list/index.js'; +import { DataIngestModule } from '../modules/data-ingest/index.js'; +import { FieldProfileModule } from '../modules/field-profile/index.js'; +import { DashboardModule } from '../modules/dashboard/index.js'; +import { CarbonEstimatorModule } from '../modules/carbon-estimator/index.js'; +import { sessionSet, sessionGet, KEYS } from '../utils/storage.js'; + +// ── Module registry ─────────────────────────────────────────────────────────── +const MODULES = [ + ReadingListModule(), + DataIngestModule(), + FieldProfileModule(), + DashboardModule(), + CarbonEstimatorModule(), +]; + +const moduleMap = Object.fromEntries(MODULES.map((m) => [m.id, m])); +let activeModuleId = 'reading-list'; + +// ── Tab navigation ──────────────────────────────────────────────────────────── +function setupTabs() { + document.querySelectorAll('.tab-btn').forEach((btn) => { + btn.addEventListener('click', () => activateTab(btn.dataset.tab)); + }); +} + +async function activateTab(id) { + if (!moduleMap[id]) return; + activeModuleId = id; + + document.querySelectorAll('.tab-btn').forEach((btn) => { + btn.classList.toggle('active-tab', btn.dataset.tab === id); + }); + + const main = document.getElementById('main-content'); + main.innerHTML = ''; + await moduleMap[id].render(main); +} + +// ── Settings panel ──────────────────────────────────────────────────────────── +function setupSettings() { + const btn = document.getElementById('btn-settings'); + const panel = document.getElementById('settings-panel'); + const saveBtn = document.getElementById('btn-save-key'); + const input = document.getElementById('api-key-input'); + const status = document.getElementById('api-key-status'); + + btn.addEventListener('click', async () => { + panel.classList.toggle('hidden'); + if (!panel.classList.contains('hidden')) { + const existing = await sessionGet(KEYS.API_KEY); + if (existing) { + input.value = ''; + input.placeholder = 'Key set — enter new key to replace'; + status.textContent = '✓ API key is active this session'; + } + } + }); + + saveBtn.addEventListener('click', async () => { + const key = input.value.trim(); + if (!key.startsWith('sk-ant-')) { + status.textContent = 'Key must start with sk-ant-'; + return; + } + await chrome.runtime.sendMessage({ type: 'SET_API_KEY', payload: { key } }); + input.value = ''; + input.placeholder = 'Key set — enter new key to replace'; + status.textContent = '✓ Saved for this session'; + }); +} + +// ── Keepalive port (prevents service worker from being killed) ──────────────── +function keepAlive() { + try { + const port = chrome.runtime.connect({ name: 'keepalive' }); + port.onDisconnect.addListener(() => { + setTimeout(keepAlive, 5000); + }); + } catch (_) {} +} + +// ── Init ────────────────────────────────────────────────────────────────────── +document.addEventListener('DOMContentLoaded', async () => { + setupTabs(); + setupSettings(); + keepAlive(); + await activateTab(activeModuleId); +}); diff --git a/agrifine-extension/src/sidebar/sidebar.css b/agrifine-extension/src/sidebar/sidebar.css new file mode 100644 index 0000000000..a4b1429033 --- /dev/null +++ b/agrifine-extension/src/sidebar/sidebar.css @@ -0,0 +1,60 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +/* Active tab highlight */ +.tab-btn.active-tab { + @apply text-agri-700; +} + +.tab-btn.active-tab svg { + @apply stroke-agri-700; +} + +/* Card styles shared across modules */ +.agri-card { + @apply bg-white rounded-xl shadow-sm border border-gray-100 p-4 mb-3; +} + +.agri-card:hover { + @apply shadow-md; +} + +/* Tag pill */ +.tag-pill { + @apply inline-block text-xs px-2 py-0.5 rounded-full bg-agri-100 text-agri-800 font-medium mr-1 mb-1; +} + +/* Section heading */ +.section-heading { + @apply text-xs uppercase tracking-widest font-semibold text-gray-400 mb-2 px-4 pt-4; +} + +/* Empty state */ +.empty-state { + @apply flex flex-col items-center justify-center py-16 text-gray-400 text-sm text-center px-6; +} + +/* Spinner */ +.spinner { + @apply inline-block w-5 h-5 border-2 border-agri-300 border-t-agri-700 rounded-full animate-spin; +} + +/* Coming soon badge */ +.coming-soon { + @apply inline-block text-xs px-2 py-0.5 rounded-full bg-earth-100 text-earth-700 font-semibold; +} + +/* Scrollbar styling */ +::-webkit-scrollbar { + width: 4px; +} + +::-webkit-scrollbar-track { + background: transparent; +} + +::-webkit-scrollbar-thumb { + background: #bbf7d0; + border-radius: 2px; +} diff --git a/agrifine-extension/src/sidebar/sidebar.html b/agrifine-extension/src/sidebar/sidebar.html new file mode 100644 index 0000000000..db557ca9dd --- /dev/null +++ b/agrifine-extension/src/sidebar/sidebar.html @@ -0,0 +1,94 @@ + + + + + + Agrifine + + + + + +
+
+ 🌾 Agrifine +
+ +
+ + + + + +
+ +
+ + + + + + + diff --git a/agrifine-extension/src/utils/api.js b/agrifine-extension/src/utils/api.js new file mode 100644 index 0000000000..dc09b16bbb --- /dev/null +++ b/agrifine-extension/src/utils/api.js @@ -0,0 +1,66 @@ +import { sessionGet, KEYS } from './storage.js'; + +const ANTHROPIC_URL = 'https://api.anthropic.com/v1/messages'; +const MODEL = 'claude-sonnet-4-6'; + +/** + * Send a message to the Anthropic API via the background service worker. + * Content scripts and sidebar cannot call external APIs directly due to CSP, + * so all API calls are proxied through the background worker. + */ +export async function callAnthropic({ system, userMessage, maxTokens = 1024 }) { + return new Promise((resolve, reject) => { + chrome.runtime.sendMessage( + { type: 'ANTHROPIC_REQUEST', payload: { system, userMessage, maxTokens } }, + (response) => { + if (chrome.runtime.lastError) { + reject(new Error(chrome.runtime.lastError.message)); + return; + } + if (response?.error) { + reject(new Error(response.error)); + return; + } + resolve(response?.text ?? ''); + } + ); + }); +} + +/** + * Direct fetch from background worker — keeps API key off content scripts. + */ +export async function fetchAnthropic({ system, userMessage, maxTokens = 1024 }) { + const apiKey = await sessionGet(KEYS.API_KEY); + if (!apiKey) throw new Error('No API key set. Open Agrifine settings to add your key.'); + + const body = { + model: MODEL, + max_tokens: maxTokens, + system, + messages: [{ role: 'user', content: userMessage }], + }; + + const res = await fetch(ANTHROPIC_URL, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'x-api-key': apiKey, + 'anthropic-version': '2023-06-01', + }, + body: JSON.stringify(body), + }); + + if (!res.ok) { + const errText = await res.text(); + throw new Error(`Anthropic API error ${res.status}: ${errText}`); + } + + const data = await res.json(); + return data.content?.[0]?.text ?? ''; +} + +export const AGRICULTURE_TAGS = [ + 'agriculture', 'equipment', 'land', 'carbon', + 'USDA', 'dairy', 'finance', 'weather', +]; diff --git a/agrifine-extension/src/utils/storage.js b/agrifine-extension/src/utils/storage.js new file mode 100644 index 0000000000..f5f6b95e86 --- /dev/null +++ b/agrifine-extension/src/utils/storage.js @@ -0,0 +1,136 @@ +/** + * Agrifine storage schema + * + * chrome.storage.local keys: + * agrifine_reading_list — Array + * agrifine_ingested_files — Array + * agrifine_field_profiles — Array + * agrifine_settings — Settings + * + * chrome.storage.session keys: + * agrifine_api_key — string (never persisted to local) + */ + +export const KEYS = { + READING_LIST: 'agrifine_reading_list', + INGESTED_FILES: 'agrifine_ingested_files', + FIELD_PROFILES: 'agrifine_field_profiles', + SETTINGS: 'agrifine_settings', + API_KEY: 'agrifine_api_key', // session only +}; + +// ── Generic helpers ────────────────────────────────────────────────────────── + +export async function localGet(key) { + return new Promise((resolve) => { + chrome.storage.local.get(key, (result) => resolve(result[key] ?? null)); + }); +} + +export async function localSet(key, value) { + return new Promise((resolve) => { + chrome.storage.local.set({ [key]: value }, resolve); + }); +} + +export async function sessionGet(key) { + return new Promise((resolve) => { + chrome.storage.session.get(key, (result) => resolve(result[key] ?? null)); + }); +} + +export async function sessionSet(key, value) { + return new Promise((resolve) => { + chrome.storage.session.set({ [key]: value }, resolve); + }); +} + +// ── Reading List ───────────────────────────────────────────────────────────── + +export async function getReadingList() { + return (await localGet(KEYS.READING_LIST)) ?? []; +} + +export async function saveReadingItem(item) { + const list = await getReadingList(); + list.unshift(item); + await localSet(KEYS.READING_LIST, list); +} + +export async function deleteReadingItem(id) { + const list = await getReadingList(); + await localSet(KEYS.READING_LIST, list.filter((i) => i.id !== id)); +} + +// ── Ingested Files ─────────────────────────────────────────────────────────── + +export async function getIngestedFiles() { + return (await localGet(KEYS.INGESTED_FILES)) ?? []; +} + +export async function saveIngestedFile(file) { + const files = await getIngestedFiles(); + files.unshift(file); + await localSet(KEYS.INGESTED_FILES, files); +} + +export async function deleteIngestedFile(id) { + const files = await getIngestedFiles(); + await localSet(KEYS.INGESTED_FILES, files.filter((f) => f.id !== id)); +} + +// ── Field Profiles ─────────────────────────────────────────────────────────── + +export async function getFieldProfiles() { + return (await localGet(KEYS.FIELD_PROFILES)) ?? []; +} + +export async function saveFieldProfile(profile) { + const profiles = await getFieldProfiles(); + const idx = profiles.findIndex((p) => p.id === profile.id); + if (idx >= 0) { + profiles[idx] = profile; + } else { + profiles.unshift(profile); + } + await localSet(KEYS.FIELD_PROFILES, profiles); +} + +export async function deleteFieldProfile(id) { + const profiles = await getFieldProfiles(); + await localSet(KEYS.FIELD_PROFILES, profiles.filter((p) => p.id !== id)); +} + +// ── Settings ───────────────────────────────────────────────────────────────── + +export async function getSettings() { + return (await localGet(KEYS.SETTINGS)) ?? { theme: 'light', defaultState: 'all' }; +} + +export async function saveSettings(patch) { + const current = await getSettings(); + await localSet(KEYS.SETTINGS, { ...current, ...patch }); +} + +// ── Context bundle (used as AI system context) ─────────────────────────────── + +export async function buildContextBundle() { + const list = await getReadingList(); + const profiles = await getFieldProfiles(); + + const readingCtx = list.slice(0, 20).map((item) => + `[${item.tags?.join(', ') ?? 'general'}] ${item.title}: ${item.summary ?? ''}` + ).join('\n'); + + const fieldCtx = profiles.map((p) => + `Field "${p.name}" (${p.acres ?? '?'} ac, ${p.soilType ?? 'unknown soil'}): ${p.notes ?? ''}` + ).join('\n'); + + return [ + 'USER READING LIST CONTEXT:', + readingCtx || '(none)', + '', + 'FIELD PROFILES:', + fieldCtx || '(none)', + ].join('\n'); +} diff --git a/agrifine-extension/tailwind.config.js b/agrifine-extension/tailwind.config.js new file mode 100644 index 0000000000..9c7b05ed17 --- /dev/null +++ b/agrifine-extension/tailwind.config.js @@ -0,0 +1,35 @@ +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: ['./src/**/*.{js,html}'], + theme: { + extend: { + colors: { + agri: { + 50: '#f0fdf4', + 100: '#dcfce7', + 200: '#bbf7d0', + 300: '#86efac', + 400: '#4ade80', + 500: '#22c55e', + 600: '#16a34a', + 700: '#15803d', + 800: '#166534', + 900: '#14532d', + }, + earth: { + 50: '#fdf8f0', + 100: '#fbefd8', + 200: '#f5d9a8', + 300: '#eebb6a', + 400: '#e59b38', + 500: '#d97f1a', + 600: '#c06212', + 700: '#9f4b12', + 800: '#813c15', + 900: '#6a3214', + }, + }, + }, + }, + plugins: [], +}; diff --git a/agrifine-extension/webpack/webpack.config.js b/agrifine-extension/webpack/webpack.config.js new file mode 100644 index 0000000000..046396403b --- /dev/null +++ b/agrifine-extension/webpack/webpack.config.js @@ -0,0 +1,57 @@ +const path = require('path'); +const CopyPlugin = require('copy-webpack-plugin'); +const MiniCssExtractPlugin = require('mini-css-extract-plugin'); + +const rootDir = path.resolve(__dirname, '..'); +const srcDir = path.join(rootDir, 'src'); +const distDir = path.join(rootDir, 'dist'); +const publicDir = path.join(rootDir, 'public'); + +module.exports = { + mode: process.env.NODE_ENV === 'production' ? 'production' : 'development', + devtool: process.env.NODE_ENV === 'production' ? false : 'cheap-module-source-map', + entry: { + background: path.join(srcDir, 'background', 'index.js'), + content: path.join(srcDir, 'content', 'index.js'), + sidebar: path.join(srcDir, 'sidebar', 'index.js'), + }, + output: { + path: distDir, + filename: '[name].js', + clean: true, + }, + module: { + rules: [ + { + test: /\.js$/, + exclude: /node_modules/, + use: { + loader: 'babel-loader', + options: { + presets: ['@babel/preset-env'], + }, + }, + }, + { + test: /\.css$/, + use: [MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader'], + }, + ], + }, + plugins: [ + new MiniCssExtractPlugin({ filename: '[name].css' }), + new CopyPlugin({ + patterns: [ + { from: path.join(publicDir, 'icons'), to: path.join(distDir, 'icons') }, + { from: path.join(rootDir, 'manifest.json'), to: distDir }, + { from: path.join(srcDir, 'sidebar', 'sidebar.html'), to: path.join(distDir, 'sidebar.html') }, + ], + }), + ], + resolve: { + extensions: ['.js'], + alias: { + '@': srcDir, + }, + }, +}; From 86775eacbd6a9d6f018734e38534820d8b3ae99c Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 27 Jun 2026 03:50:57 +0000 Subject: [PATCH 02/17] fix: correct manifest paths for dist/ load context --- agrifine-extension/manifest.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/agrifine-extension/manifest.json b/agrifine-extension/manifest.json index a24c3d3f61..54b3235900 100644 --- a/agrifine-extension/manifest.json +++ b/agrifine-extension/manifest.json @@ -14,18 +14,18 @@ "" ], "background": { - "service_worker": "dist/background.js", + "service_worker": "background.js", "type": "module" }, "content_scripts": [ { "matches": [""], - "js": ["dist/content.js"], + "js": ["content.js"], "run_at": "document_idle" } ], "side_panel": { - "default_path": "dist/sidebar.html" + "default_path": "sidebar.html" }, "action": { "default_title": "Open Agrifine", From 6f94bd7c79196d98b95acf2a7dc3c2a3f958cca3 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 27 Jun 2026 03:58:28 +0000 Subject: [PATCH 03/17] =?UTF-8?q?feat:=20add=20AgriAgent=20=E2=80=94=20rea?= =?UTF-8?q?l=20agentic=20loop=20with=20tool=20use?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Multi-step AI agent in src/ag-refine/: - agent.js: Claude tool-use loop (up to 10 iterations), streams events to UI as it thinks and calls tools - tools.js: 6 live tools — get_reading_list, get_field_profiles, get_ingested_files, get_weather (Open-Meteo), lookup_usda_soil (USDA SDA API), calculate_gdd - index.js: chat UI with streaming tool-call display, suggested prompts, clear conversation - Wired as new Agent tab in sidebar (6th tab) - Also fixes manifest.json paths (dist/ prefix removed) Co-Authored-By: Claude Sonnet 4.6 Claude-Session: https://claude.ai/code/session_01KBD2dN2KEjzz3UQFa9hEpu --- agrifine-extension/src/ag-refine/agent.js | 127 ++++++++ agrifine-extension/src/ag-refine/index.js | 234 +++++++++++++++ agrifine-extension/src/ag-refine/tools.js | 304 ++++++++++++++++++++ agrifine-extension/src/sidebar/index.js | 2 + agrifine-extension/src/sidebar/sidebar.html | 9 + 5 files changed, 676 insertions(+) create mode 100644 agrifine-extension/src/ag-refine/agent.js create mode 100644 agrifine-extension/src/ag-refine/index.js create mode 100644 agrifine-extension/src/ag-refine/tools.js diff --git a/agrifine-extension/src/ag-refine/agent.js b/agrifine-extension/src/ag-refine/agent.js new file mode 100644 index 0000000000..75af4ba476 --- /dev/null +++ b/agrifine-extension/src/ag-refine/agent.js @@ -0,0 +1,127 @@ +import { sessionGet, KEYS, buildContextBundle } from '../utils/storage.js'; +import { TOOL_DEFINITIONS, executeTool } from './tools.js'; + +const MODEL = 'claude-sonnet-4-6'; +const ANTHROPIC_URL = 'https://api.anthropic.com/v1/messages'; +const MAX_ITERATIONS = 10; + +/** + * AgrifineAgent — agentic loop with tool use. + * + * Runs entirely in the sidebar context via the background worker proxy. + * Each call to run() streams back events via an onEvent callback so the + * UI can update incrementally as the agent thinks and calls tools. + */ +export class AgrifineAgent { + constructor({ onEvent }) { + this.onEvent = onEvent; // ({ type, data }) => void + } + + async run(userMessage) { + const apiKey = await sessionGet(KEYS.API_KEY); + if (!apiKey) { + this.onEvent({ type: 'error', data: 'No API key set. Open ⚙ Settings to add your Anthropic key.' }); + return; + } + + const contextBundle = await buildContextBundle(); + + const systemPrompt = [ + 'You are AgriAgent, an expert AI assistant for farm operations management.', + 'You have access to the user\'s farm data through tools — always use them before answering.', + 'When answering questions about fields, weather, yields, or finances: first query the relevant data, then synthesize a clear answer.', + 'Be specific: cite field names, dates, acreage, and numbers from the actual data.', + 'For weather queries on a field, always look up the field profile first to get coordinates.', + '', + 'FARM CONTEXT (reading list summaries + field profiles):', + contextBundle, + ].join('\n'); + + const messages = [{ role: 'user', content: userMessage }]; + + this.onEvent({ type: 'thinking', data: 'Analysing your question…' }); + + for (let i = 0; i < MAX_ITERATIONS; i++) { + const body = { + model: MODEL, + max_tokens: 2048, + system: systemPrompt, + tools: TOOL_DEFINITIONS, + messages, + }; + + let response; + try { + response = await this._callAPI(apiKey, body); + } catch (err) { + this.onEvent({ type: 'error', data: err.message }); + return; + } + + // Append assistant turn + messages.push({ role: 'assistant', content: response.content }); + + if (response.stop_reason === 'end_turn') { + // Extract final text + const text = response.content + .filter((b) => b.type === 'text') + .map((b) => b.text) + .join('\n'); + this.onEvent({ type: 'answer', data: text }); + return; + } + + if (response.stop_reason === 'tool_use') { + const toolUseBlocks = response.content.filter((b) => b.type === 'tool_use'); + const toolResults = []; + + for (const block of toolUseBlocks) { + this.onEvent({ type: 'tool_call', data: { name: block.name, input: block.input } }); + + let result; + try { + result = await executeTool(block.name, block.input); + } catch (err) { + result = { error: err.message }; + } + + this.onEvent({ type: 'tool_result', data: { name: block.name, result } }); + + toolResults.push({ + type: 'tool_result', + tool_use_id: block.id, + content: JSON.stringify(result), + }); + } + + messages.push({ role: 'user', content: toolResults }); + continue; + } + + // Unexpected stop reason + this.onEvent({ type: 'error', data: `Unexpected stop reason: ${response.stop_reason}` }); + return; + } + + this.onEvent({ type: 'error', data: 'Agent reached maximum iterations without completing.' }); + } + + async _callAPI(apiKey, body) { + const res = await fetch(ANTHROPIC_URL, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'x-api-key': apiKey, + 'anthropic-version': '2023-06-01', + }, + body: JSON.stringify(body), + }); + + if (!res.ok) { + const text = await res.text(); + throw new Error(`Anthropic API ${res.status}: ${text}`); + } + + return res.json(); + } +} diff --git a/agrifine-extension/src/ag-refine/index.js b/agrifine-extension/src/ag-refine/index.js new file mode 100644 index 0000000000..d870629581 --- /dev/null +++ b/agrifine-extension/src/ag-refine/index.js @@ -0,0 +1,234 @@ +import { AgrifineAgent } from './agent.js'; + +const TOOL_ICONS = { + get_reading_list: '📖', + get_field_profiles: '🌱', + get_ingested_files: '📄', + get_weather: '🌤️', + lookup_usda_soil: '🏛️', + calculate_gdd: '📊', +}; + +const SUGGESTED_PROMPTS = [ + 'What are my current field conditions and harvest windows?', + 'Which fields have the best soil for carbon sequestration?', + 'Summarise all my farm data and flag any issues', + 'What does the 7-day weather look like for my fields?', + 'What USDA programs might I qualify for based on my fields?', +]; + +export function AgRefineModule() { + let messages = []; + let isRunning = false; + + return { + id: 'ag-refine', + label: 'AgriAgent', + + async render(container) { + container.innerHTML = ` +
+ + +
+
+ 🤖 +

AgriAgent

+ AI Agent +
+

Multi-step reasoning over all your farm data

+
+ + +
+ + +
+

Try asking…

+
+ ${SUGGESTED_PROMPTS.map((p) => ` + `).join('')} +
+
+ + +
+
+ + +
+ +
+
+ `; + + this._bindEvents(container); + this._renderMessages(container); + }, + + _bindEvents(container) { + const input = container.querySelector('#agent-input'); + const sendBtn = container.querySelector('#agent-send'); + + const send = () => { + const text = input.value.trim(); + if (!text || isRunning) return; + input.value = ''; + this._runAgent(text, container); + }; + + sendBtn.addEventListener('click', send); + input.addEventListener('keydown', (e) => { + if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); send(); } + }); + + container.querySelectorAll('.suggest-btn').forEach((btn) => { + btn.addEventListener('click', () => { + input.value = btn.textContent.trim(); + send(); + }); + }); + + container.querySelector('#agent-clear').addEventListener('click', () => { + messages = []; + isRunning = false; + this._renderMessages(container); + }); + }, + + async _runAgent(userText, container) { + if (isRunning) return; + isRunning = true; + + // Hide suggestions + container.querySelector('#agent-suggestions')?.classList.add('hidden'); + + // Add user message + messages.push({ role: 'user', text: userText }); + this._renderMessages(container); + + // Thinking placeholder + const thinkingId = `thinking_${Date.now()}`; + messages.push({ role: 'thinking', id: thinkingId, steps: [] }); + this._renderMessages(container); + + const thinkingMsg = messages[messages.length - 1]; + + const agent = new AgrifineAgent({ + onEvent: ({ type, data }) => { + if (type === 'thinking') { + thinkingMsg.steps.push({ type: 'status', text: data }); + } else if (type === 'tool_call') { + thinkingMsg.steps.push({ + type: 'tool', + icon: TOOL_ICONS[data.name] ?? '🔧', + name: data.name.replace(/_/g, ' '), + input: JSON.stringify(data.input), + }); + } else if (type === 'tool_result') { + const last = thinkingMsg.steps[thinkingMsg.steps.length - 1]; + if (last?.type === 'tool') last.done = true; + } else if (type === 'answer') { + // Replace thinking bubble with final answer + const idx = messages.findIndex((m) => m.id === thinkingId); + if (idx >= 0) messages.splice(idx, 1); + messages.push({ role: 'assistant', text: data }); + isRunning = false; + } else if (type === 'error') { + const idx = messages.findIndex((m) => m.id === thinkingId); + if (idx >= 0) messages.splice(idx, 1); + messages.push({ role: 'error', text: data }); + isRunning = false; + } + this._renderMessages(container); + }, + }); + + await agent.run(userText); + }, + + _renderMessages(container) { + const chat = container.querySelector('#agent-chat'); + if (!chat) return; + + if (messages.length === 0) { + chat.innerHTML = ''; + container.querySelector('#agent-suggestions')?.classList.remove('hidden'); + return; + } + + chat.innerHTML = messages.map((msg) => { + if (msg.role === 'user') { + return ` +
+
+ ${escapeHtml(msg.text)} +
+
`; + } + + if (msg.role === 'thinking') { + const steps = msg.steps ?? []; + return ` +
+ ${steps.map((step) => { + if (step.type === 'status') { + return `
+ ${escapeHtml(step.text)} +
`; + } + if (step.type === 'tool') { + return `
+ ${step.icon} + ${step.name} + ${step.done ? '' : ''} +
`; + } + return ''; + }).join('')} + ${steps.length === 0 ? '
Starting…
' : ''} +
`; + } + + if (msg.role === 'assistant') { + return ` +
+
🤖
+
+ ${escapeHtml(msg.text)} +
+
`; + } + + if (msg.role === 'error') { + return ` +
+ ⚠️ ${escapeHtml(msg.text)} +
`; + } + + return ''; + }).join(''); + + // Scroll to bottom + chat.scrollTop = chat.scrollHeight; + }, + }; +} + +function escapeHtml(str) { + return String(str) + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"'); +} diff --git a/agrifine-extension/src/ag-refine/tools.js b/agrifine-extension/src/ag-refine/tools.js new file mode 100644 index 0000000000..6d693cd6b7 --- /dev/null +++ b/agrifine-extension/src/ag-refine/tools.js @@ -0,0 +1,304 @@ +import { getReadingList, getIngestedFiles, getFieldProfiles } from '../utils/storage.js'; + +// ── Tool definitions sent to Claude ────────────────────────────────────────── + +export const TOOL_DEFINITIONS = [ + { + name: 'get_reading_list', + description: 'Retrieve saved web pages from the user\'s reading list. Can filter by tag.', + input_schema: { + type: 'object', + properties: { + tag: { + type: 'string', + description: 'Optional tag to filter by: agriculture, equipment, land, carbon, USDA, dairy, finance, weather', + }, + }, + required: [], + }, + }, + { + name: 'get_field_profiles', + description: 'Retrieve all farm field profiles including acreage, soil type, coordinates, crop history, and notes.', + input_schema: { + type: 'object', + properties: { + field_name: { + type: 'string', + description: 'Optional field name to filter by (partial match)', + }, + }, + required: [], + }, + }, + { + name: 'get_ingested_files', + description: 'Retrieve all uploaded and parsed farm data files (CSV, Excel, PDF). Returns structured JSON extracted from each file.', + input_schema: { + type: 'object', + properties: { + type: { + type: 'string', + enum: ['CSV', 'Excel', 'PDF'], + description: 'Optional file type filter', + }, + }, + required: [], + }, + }, + { + name: 'get_weather', + description: 'Fetch current conditions, 7-day forecast, and calculate Growing Degree Days (GDD) for a field location using Open-Meteo (free, no key required).', + input_schema: { + type: 'object', + properties: { + latitude: { + type: 'number', + description: 'Latitude of the field', + }, + longitude: { + type: 'number', + description: 'Longitude of the field', + }, + field_name: { + type: 'string', + description: 'Human-readable field name for context', + }, + }, + required: ['latitude', 'longitude'], + }, + }, + { + name: 'lookup_usda_soil', + description: 'Look up soil data from the USDA Web Soil Survey API by coordinates. Returns soil series, texture, organic matter, and drainage class.', + input_schema: { + type: 'object', + properties: { + latitude: { + type: 'number', + description: 'Latitude', + }, + longitude: { + type: 'number', + description: 'Longitude', + }, + }, + required: ['latitude', 'longitude'], + }, + }, + { + name: 'calculate_gdd', + description: 'Calculate Growing Degree Days from temperature data. Uses base temp of 50°F for forage crops.', + input_schema: { + type: 'object', + properties: { + daily_highs: { + type: 'array', + items: { type: 'number' }, + description: 'Array of daily high temperatures in Fahrenheit', + }, + daily_lows: { + type: 'array', + items: { type: 'number' }, + description: 'Array of daily low temperatures in Fahrenheit', + }, + base_temp: { + type: 'number', + description: 'Base temperature in °F (default 50 for forage crops)', + }, + }, + required: ['daily_highs', 'daily_lows'], + }, + }, +]; + +// ── Tool implementations ────────────────────────────────────────────────────── + +export async function executeTool(name, input) { + switch (name) { + case 'get_reading_list': + return toolGetReadingList(input); + case 'get_field_profiles': + return toolGetFieldProfiles(input); + case 'get_ingested_files': + return toolGetIngestedFiles(input); + case 'get_weather': + return toolGetWeather(input); + case 'lookup_usda_soil': + return toolLookupUSDAsoil(input); + case 'calculate_gdd': + return toolCalculateGDD(input); + default: + return { error: `Unknown tool: ${name}` }; + } +} + +async function toolGetReadingList({ tag } = {}) { + const list = await getReadingList(); + const filtered = tag ? list.filter((i) => i.tags?.includes(tag)) : list; + return { + count: filtered.length, + items: filtered.slice(0, 30).map((i) => ({ + title: i.title, + url: i.url, + summary: i.summary, + tags: i.tags, + savedAt: i.savedAt, + })), + }; +} + +async function toolGetFieldProfiles({ field_name } = {}) { + const profiles = await getFieldProfiles(); + const filtered = field_name + ? profiles.filter((p) => p.name.toLowerCase().includes(field_name.toLowerCase())) + : profiles; + return { + count: filtered.length, + profiles: filtered.map((p) => ({ + id: p.id, + name: p.name, + acres: p.acres, + soilType: p.soilType, + cluId: p.cluId, + coordinates: p.coordinates, + notes: p.notes, + cropHistory: p.cropHistory, + harvestRecords: p.harvestRecords, + createdAt: p.createdAt, + })), + }; +} + +async function toolGetIngestedFiles({ type } = {}) { + const files = await getIngestedFiles(); + const filtered = type ? files.filter((f) => f.type === type) : files; + return { + count: filtered.length, + files: filtered.map((f) => ({ + filename: f.filename, + type: f.type, + uploadedAt: f.uploadedAt, + structuredData: f.structuredData, + })), + }; +} + +async function toolGetWeather({ latitude, longitude, field_name = 'field' }) { + const url = new URL('https://api.open-meteo.com/v1/forecast'); + url.searchParams.set('latitude', latitude); + url.searchParams.set('longitude', longitude); + url.searchParams.set('current', 'temperature_2m,precipitation,wind_speed_10m,weather_code'); + url.searchParams.set('daily', 'temperature_2m_max,temperature_2m_min,precipitation_sum,precipitation_probability_max'); + url.searchParams.set('temperature_unit', 'fahrenheit'); + url.searchParams.set('wind_speed_unit', 'mph'); + url.searchParams.set('precipitation_unit', 'inch'); + url.searchParams.set('forecast_days', '7'); + url.searchParams.set('timezone', 'auto'); + + const res = await fetch(url.toString()); + if (!res.ok) throw new Error(`Open-Meteo error: ${res.status}`); + const data = await res.json(); + + const current = data.current; + const daily = data.daily; + + // GDD accumulation from forecast + const gddDays = daily.temperature_2m_max.map((hi, i) => { + const lo = daily.temperature_2m_min[i]; + return Math.max(0, ((hi + lo) / 2) - 50); + }); + const totalGDD = gddDays.reduce((a, b) => a + b, 0); + + // Rain alert: >0.5 inch in next 48h + const rainAlert = (daily.precipitation_sum[0] ?? 0) + (daily.precipitation_sum[1] ?? 0) > 0.5; + + // Harvest window + const avgRainProb = (daily.precipitation_probability_max.slice(0, 3).reduce((a, b) => a + b, 0) / 3); + const harvestWindow = avgRainProb < 20 ? 'GREEN' : avgRainProb < 50 ? 'YELLOW' : 'RED'; + + return { + field: field_name, + coordinates: { latitude, longitude }, + current: { + temperature_f: current.temperature_2m, + precipitation_in: current.precipitation, + wind_mph: current.wind_speed_10m, + }, + forecast_7day: daily.time.map((date, i) => ({ + date, + high_f: daily.temperature_2m_max[i], + low_f: daily.temperature_2m_min[i], + precip_in: daily.precipitation_sum[i], + rain_probability_pct: daily.precipitation_probability_max[i], + gdd: gddDays[i].toFixed(1), + })), + gdd_7day_total: totalGDD.toFixed(1), + rain_alert_48h: rainAlert, + harvest_window: harvestWindow, + }; +} + +async function toolLookupUSDAsoil({ latitude, longitude }) { + // USDA Web Soil Survey SDA REST API + const query = `SELECT mapunit.muname, component.compname, component.comppct_r, + component.taxorder, component.taxsubgrp, chorizon.texture, chorizon.om_r, + chorizon.drainagecl + FROM mapunit + INNER JOIN component ON mapunit.mukey = component.mukey + INNER JOIN chorizon ON component.cokey = chorizon.cokey + WHERE mu_lks.mukey IN ( + SELECT * FROM SDA_Get_Mukey_from_intersection_with_WktWgs84( + 'point(${longitude} ${latitude})') + ) + AND component.majcompflag = 'Yes' + ORDER BY component.comppct_r DESC`; + + try { + const res = await fetch('https://sdmdataaccess.sc.egov.usda.gov/TABULAR/post.rest', { + method: 'POST', + headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, + body: `request=query&query=${encodeURIComponent(query)}&format=JSON`, + }); + const data = await res.json(); + const rows = data.Table ?? []; + return { + coordinates: { latitude, longitude }, + soil_data: rows.slice(0, 5).map((r) => ({ + map_unit: r[0], + component: r[1], + percent: r[2], + tax_order: r[3], + subgroup: r[4], + texture: r[5], + organic_matter_pct: r[6], + drainage_class: r[7], + })), + }; + } catch (_) { + return { + coordinates: { latitude, longitude }, + note: 'USDA SDA API unavailable — soil data requires network access from background worker', + }; + } +} + +function toolCalculateGDD({ daily_highs, daily_lows, base_temp = 50 }) { + const gdd_per_day = daily_highs.map((hi, i) => { + const lo = daily_lows[i] ?? hi; + const avg = (hi + lo) / 2; + return Math.max(0, avg - base_temp); + }); + const total = gdd_per_day.reduce((a, b) => a + b, 0); + return { + base_temp_f: base_temp, + days: gdd_per_day.length, + gdd_per_day: gdd_per_day.map((g) => parseFloat(g.toFixed(1))), + total_gdd: parseFloat(total.toFixed(1)), + interpretation: + total < 200 ? 'Early growth stage' : + total < 500 ? 'Vegetative growth' : + total < 900 ? 'Approaching harvest window' : + 'Harvest recommended', + }; +} diff --git a/agrifine-extension/src/sidebar/index.js b/agrifine-extension/src/sidebar/index.js index 72d1209ea9..2c88fa189b 100644 --- a/agrifine-extension/src/sidebar/index.js +++ b/agrifine-extension/src/sidebar/index.js @@ -4,6 +4,7 @@ import { DataIngestModule } from '../modules/data-ingest/index.js'; import { FieldProfileModule } from '../modules/field-profile/index.js'; import { DashboardModule } from '../modules/dashboard/index.js'; import { CarbonEstimatorModule } from '../modules/carbon-estimator/index.js'; +import { AgRefineModule } from '../ag-refine/index.js'; import { sessionSet, sessionGet, KEYS } from '../utils/storage.js'; // ── Module registry ─────────────────────────────────────────────────────────── @@ -13,6 +14,7 @@ const MODULES = [ FieldProfileModule(), DashboardModule(), CarbonEstimatorModule(), + AgRefineModule(), ]; const moduleMap = Object.fromEntries(MODULES.map((m) => [m.id, m])); diff --git a/agrifine-extension/src/sidebar/sidebar.html b/agrifine-extension/src/sidebar/sidebar.html index db557ca9dd..f9a08805ba 100644 --- a/agrifine-extension/src/sidebar/sidebar.html +++ b/agrifine-extension/src/sidebar/sidebar.html @@ -86,6 +86,15 @@ Carbon + + From 3e16c962c61524feefe8cd9f893d16bf33a301fc Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 27 Jun 2026 04:05:08 +0000 Subject: [PATCH 04/17] feat: add run-agrifine-extension skill + Playwright driver Driver (.claude/skills/run-agrifine-extension/driver.mjs): - Launches Chrome with unpacked extension via Playwright persistent context - Stubs chrome.* APIs so sidebar renders headlessly without real extension context - REPL commands: ss, tab, click, type, eval, quit - Screenshots land in screenshots/ - Verified: all 6 tabs render correctly (Reading, Ingest, Fields, Dashboard, Carbon, Agent) SKILL.md documents agent path first, gotchas, and troubleshooting from actual execution in this container. Co-Authored-By: Claude Sonnet 4.6 Claude-Session: https://claude.ai/code/session_01KBD2dN2KEjzz3UQFa9hEpu --- .../skills/run-agrifine-extension/SKILL.md | 87 ++++++++++ .../skills/run-agrifine-extension/driver.mjs | 162 ++++++++++++++++++ agrifine-extension/package-lock.json | 48 ++++++ agrifine-extension/package.json | 1 + agrifine-extension/screenshots/ag-refine.png | Bin 0 -> 53214 bytes agrifine-extension/screenshots/dashboard.png | Bin 0 -> 35371 bytes .../screenshots/data-ingest.png | Bin 0 -> 33235 bytes .../screenshots/field-profile.png | Bin 0 -> 26746 bytes .../screenshots/reading-list.png | Bin 0 -> 33490 bytes .../screenshots/sidebar_initial.png | Bin 0 -> 33496 bytes 10 files changed, 298 insertions(+) create mode 100644 agrifine-extension/.claude/skills/run-agrifine-extension/SKILL.md create mode 100644 agrifine-extension/.claude/skills/run-agrifine-extension/driver.mjs create mode 100644 agrifine-extension/screenshots/ag-refine.png create mode 100644 agrifine-extension/screenshots/dashboard.png create mode 100644 agrifine-extension/screenshots/data-ingest.png create mode 100644 agrifine-extension/screenshots/field-profile.png create mode 100644 agrifine-extension/screenshots/reading-list.png create mode 100644 agrifine-extension/screenshots/sidebar_initial.png diff --git a/agrifine-extension/.claude/skills/run-agrifine-extension/SKILL.md b/agrifine-extension/.claude/skills/run-agrifine-extension/SKILL.md new file mode 100644 index 0000000000..2df452b302 --- /dev/null +++ b/agrifine-extension/.claude/skills/run-agrifine-extension/SKILL.md @@ -0,0 +1,87 @@ +--- +name: run-agrifine-extension +description: Run, build, launch, screenshot, or drive the Agrifine browser extension UI. Use when asked to start, test, verify, or take a screenshot of the extension sidebar or any of its tabs (reading list, data ingest, field profiles, dashboard, AgriAgent). +--- + +# run-agrifine-extension + +Agrifine is a Manifest V3 Chrome extension with a persistent sidebar panel. The sidebar (`dist/sidebar.html`) is driven headlessly via Playwright using the pre-installed Chromium at `/opt/pw-browsers`. A `chrome.*` API stub lets the page render without a real extension context. + +All paths below are relative to `agrifine-extension/` (the unit root). + +## Prerequisites + +Node.js 18+ and Playwright are already in `node_modules` (added as devDependency). Set this env var for every command: + +```bash +export PLAYWRIGHT_BROWSERS_PATH=/opt/pw-browsers +``` + +## Build + +```bash +npm run build +# → dist/ produced, webpack compiled successfully +``` + +## Run — agent path (driver) + +Driver: `.claude/skills/run-agrifine-extension/driver.mjs` +Screenshots land in: `screenshots/` + +**Single command:** +```bash +PLAYWRIGHT_BROWSERS_PATH=/opt/pw-browsers node .claude/skills/run-agrifine-extension/driver.mjs "ss sidebar_initial" +# → screenshots/sidebar_initial.png +``` + +**Interactive REPL:** +```bash +PLAYWRIGHT_BROWSERS_PATH=/opt/pw-browsers node .claude/skills/run-agrifine-extension/driver.mjs +# agrifine> ss reading-tab +# agrifine> tab agent +# agrifine> ss agent-tab +# agrifine> eval document.querySelector('#main-content').innerHTML.slice(0,200) +# agrifine> quit +``` + +**Available REPL commands:** + +| Command | Effect | +|---|---| +| `ss [name]` | Screenshot → `screenshots/.png` | +| `tab ` | Switch tab: `reading`, `ingest`, `fields`, `dashboard`, `carbon`, `agent` | +| `click ` | Click a CSS selector | +| `type ` | Fill an input | +| `eval ` | Evaluate JS in page context, print result | +| `quit` | Exit | + +## Verified flows (run in this container) + +```bash +# Initial sidebar — Reading List tab +PLAYWRIGHT_BROWSERS_PATH=/opt/pw-browsers node .claude/skills/run-agrifine-extension/driver.mjs "ss sidebar_initial" + +# All 5 tabs +PLAYWRIGHT_BROWSERS_PATH=/opt/pw-browsers node -e "..." # (see driver source) +``` + +Screenshots confirmed: green header, bottom tab bar with 6 tabs (Reading, Ingest, Fields, Dashboard, Carbon, Agent), AgriAgent chat UI with suggested prompts visible. + +## Gotchas + +- **`chrome.*` APIs are stubbed** — storage reads return null, `sendMessage` returns an error object. The sidebar renders and navigates correctly; AI calls fail gracefully with "No API key set." +- **Extension loaded from `dist/`** — always `npm run build` first. The driver checks for `dist/manifest.json` and exits with a clear error if missing. +- **`PLAYWRIGHT_BROWSERS_PATH` must be set** — without it, Playwright tries to download browsers and fails. Always export it before running the driver. +- **PersistentContext required** — Chrome extensions only load in `launchPersistentContext`, not `launch`. The profile dir is passed as `''` (temp, cleaned up on exit). +- **Tabs are data-attribute driven** — selectors are `[data-tab="reading-list"]` etc. The driver maps short names (`reading`, `agent`) to full attribute values. + +## Troubleshooting + +| Error | Fix | +|---|---| +| `Cannot find package 'playwright'` | `npm install` inside `agrifine-extension/` | +| `dist/ not found` | `npm run build` | +| `Error: dist/ not found` with correct path | Check `UNIT_ROOT` in driver — must resolve to `agrifine-extension/`, 3 levels up from skill dir | +| Page blank / `#main-content` timeout | Chrome stub missing — ensure `addInitScript` runs before `goto` | +| `ERR_FILE_NOT_FOUND` for sidebar.html | Build produced it at wrong path — check `webpack.config.js` CopyPlugin target | diff --git a/agrifine-extension/.claude/skills/run-agrifine-extension/driver.mjs b/agrifine-extension/.claude/skills/run-agrifine-extension/driver.mjs new file mode 100644 index 0000000000..1c14cd1a56 --- /dev/null +++ b/agrifine-extension/.claude/skills/run-agrifine-extension/driver.mjs @@ -0,0 +1,162 @@ +#!/usr/bin/env node +/** + * Agrifine Extension driver + * Launches Chrome with the unpacked extension loaded, opens the sidebar + * page directly, and exposes a simple REPL for agent interaction. + * + * Usage: + * node driver.mjs [command] + * + * Commands (interactive REPL if none given): + * ss [file] Take screenshot → screenshots/.png + * click Click element + * tab Click tab by label (reading|ingest|fields|dashboard|carbon|agent) + * type Type into element + * eval Evaluate JS in page, print result + * quit Exit + */ + +import { chromium } from 'playwright'; +import { createInterface } from 'readline'; +import { mkdirSync, existsSync } from 'fs'; +import { resolve, dirname } from 'path'; +import { fileURLToPath } from 'url'; + +const __dir = dirname(fileURLToPath(import.meta.url)); +// Skill lives at .claude/skills/run-agrifine-extension/ +// Unit root (agrifine-extension/) is 3 levels up +const UNIT_ROOT = resolve(__dir, '..', '..', '..'); +const DIST = resolve(UNIT_ROOT, 'dist'); +const SCREENSHOTS = resolve(UNIT_ROOT, 'screenshots'); +const CHROMIUM = process.env.PLAYWRIGHT_BROWSERS_PATH + ? `${process.env.PLAYWRIGHT_BROWSERS_PATH}/chromium-1194/chrome-linux/chrome` + : '/opt/pw-browsers/chromium-1194/chrome-linux/chrome'; + +mkdirSync(SCREENSHOTS, { recursive: true }); + +async function main() { + if (!existsSync(DIST + '/manifest.json')) { + console.error('ERROR: dist/ not found. Run: npm run build'); + process.exit(1); + } + + console.log('Launching Chrome with Agrifine extension…'); + + // Chrome requires a persistent context to load extensions + const context = await chromium.launchPersistentContext('', { + executablePath: CHROMIUM, + headless: true, + args: [ + `--disable-extensions-except=${DIST}`, + `--load-extension=${DIST}`, + '--no-sandbox', + '--disable-setuid-sandbox', + '--disable-dev-shm-usage', + '--disable-gpu', + ], + }); + + // Open the sidebar HTML directly — works for visual/UI testing + // (chrome.* APIs are mocked via the stub below) + const page = await context.newPage(); + + // Stub chrome.* APIs so the page renders without a real extension context + await page.addInitScript(() => { + const store = {}; + window.chrome = { + storage: { + local: { + get: (k, cb) => cb({ [k]: null }), + set: (_o, cb) => cb && cb(), + }, + session: { + get: (k, cb) => cb({ [k]: null }), + set: (_o, cb) => cb && cb(), + }, + }, + runtime: { + sendMessage: (_msg, cb) => cb && cb({ error: 'No background in test mode' }), + connect: () => ({ onDisconnect: { addListener: () => {} } }), + lastError: null, + }, + tabs: { + query: (_q, cb) => cb([{ id: 1, url: 'https://example.com', title: 'Test Page' }]), + sendMessage: (_id, _msg, cb) => cb && cb({ text: 'test page content', title: 'Test' }), + }, + sidePanel: { setPanelBehavior: () => Promise.resolve() }, + }; + }); + + await page.goto(`file://${DIST}/sidebar.html`); + await page.waitForSelector('#main-content', { timeout: 5000 }); + console.log('Extension sidebar loaded.'); + + // Single command mode + const args = process.argv.slice(2); + if (args.length > 0) { + await runCommand(page, args.join(' ')); + await context.close(); + return; + } + + // Interactive REPL + console.log('REPL ready. Commands: ss [file] | click | tab | type | eval | quit'); + const rl = createInterface({ input: process.stdin, output: process.stdout, prompt: 'agrifine> ' }); + rl.prompt(); + rl.on('line', async (line) => { + const cmd = line.trim(); + if (!cmd) { rl.prompt(); return; } + if (cmd === 'quit' || cmd === 'exit') { await context.close(); process.exit(0); } + await runCommand(page, cmd); + rl.prompt(); + }); + rl.on('close', async () => { await context.close(); }); +} + +async function runCommand(page, cmd) { + const [verb, ...rest] = cmd.split(/\s+/); + try { + if (verb === 'ss') { + const name = rest[0] || `screenshot_${Date.now()}`; + const file = `${SCREENSHOTS}/${name.endsWith('.png') ? name : name + '.png'}`; + await page.screenshot({ path: file, fullPage: false }); + console.log(`Screenshot: ${file}`); + + } else if (verb === 'tab') { + const label = rest[0]?.toLowerCase(); + const TAB_MAP = { + reading: '[data-tab="reading-list"]', + ingest: '[data-tab="data-ingest"]', + fields: '[data-tab="field-profile"]', + dashboard: '[data-tab="dashboard"]', + carbon: '[data-tab="carbon-estimator"]', + agent: '[data-tab="ag-refine"]', + }; + const sel = TAB_MAP[label] ?? `[data-tab="${label}"]`; + await page.click(sel); + await page.waitForTimeout(300); + console.log(`Clicked tab: ${label}`); + + } else if (verb === 'click') { + await page.click(rest.join(' ')); + await page.waitForTimeout(200); + console.log('Clicked.'); + + } else if (verb === 'type') { + const [sel, ...words] = rest; + await page.fill(sel, words.join(' ')); + console.log('Typed.'); + + } else if (verb === 'eval') { + const result = await page.evaluate(rest.join(' ')); + console.log(JSON.stringify(result, null, 2)); + + } else { + console.log(`Unknown command: ${verb}. Try: ss | tab | click | type | eval | quit`); + } + } catch (err) { + console.error(`Error: ${err.message}`); + } +} + +main().catch((err) => { console.error(err); process.exit(1); }); diff --git a/agrifine-extension/package-lock.json b/agrifine-extension/package-lock.json index 1bc27def3d..bc4eb68b8a 100644 --- a/agrifine-extension/package-lock.json +++ b/agrifine-extension/package-lock.json @@ -20,6 +20,7 @@ "copy-webpack-plugin": "^12.0.2", "css-loader": "^7.1.2", "mini-css-extract-plugin": "^2.9.0", + "playwright": "^1.61.1", "postcss": "^8.4.38", "postcss-loader": "^8.1.1", "tailwindcss": "^3.4.3", @@ -4046,6 +4047,53 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/playwright": { + "version": "1.61.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.61.1.tgz", + "integrity": "sha512-DWnY5o3YbLWK4GovuAVwpqL+1VwGNdUGrRr++8j8PtQQzvAVZUIMjKQ90fY689sEJZJBbZVw1rXaOKSTitkzPQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.61.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.61.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.61.1.tgz", + "integrity": "sha512-h7Qlt6m4REp25qvIdvbDtVmD4LqVXfpRxhORv9L0jzETM05p4fuPJ3dKyuSXQxDSbXnmS79HAgi9589lGSpLkg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/playwright/node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/postcss": { "version": "8.5.15", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", diff --git a/agrifine-extension/package.json b/agrifine-extension/package.json index a33d9c5e86..2270b00928 100644 --- a/agrifine-extension/package.json +++ b/agrifine-extension/package.json @@ -16,6 +16,7 @@ "copy-webpack-plugin": "^12.0.2", "css-loader": "^7.1.2", "mini-css-extract-plugin": "^2.9.0", + "playwright": "^1.61.1", "postcss": "^8.4.38", "postcss-loader": "^8.1.1", "tailwindcss": "^3.4.3", diff --git a/agrifine-extension/screenshots/ag-refine.png b/agrifine-extension/screenshots/ag-refine.png new file mode 100644 index 0000000000000000000000000000000000000000..1c79dde609e3c844c8c7331efb95483b8b153937 GIT binary patch literal 53214 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

e(i{wO7wV}x>zzm$U8@Z{ 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�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 z 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 G=l=xyn64@S literal 0 HcmV?d00001 diff --git a/agrifine-extension/screenshots/data-ingest.png b/agrifine-extension/screenshots/data-ingest.png new file mode 100644 index 0000000000000000000000000000000000000000..742649a352422867f6c59d49526b01324ad0c164 GIT binary patch literal 33235 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 L@ssMu4j=y)A(KA> literal 0 HcmV?d00001 diff --git a/agrifine-extension/screenshots/reading-list.png b/agrifine-extension/screenshots/reading-list.png new file mode 100644 index 0000000000000000000000000000000000000000..da446221e9badd533da54aef5ad9ef36be2ecaf8 GIT binary patch literal 33490 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 literal 0 HcmV?d00001 diff --git a/agrifine-extension/screenshots/sidebar_initial.png b/agrifine-extension/screenshots/sidebar_initial.png new file mode 100644 index 0000000000000000000000000000000000000000..d2bd0204868be74c2f45ca540a9ae568957f8a5a GIT binary patch literal 33496 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 F{x3CD4dMU* literal 0 HcmV?d00001 From b7b832764dbc14ed540de4c9d8ae0ff1219963cb Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 27 Jun 2026 04:16:30 +0000 Subject: [PATCH 05/17] fix: add anthropic-dangerous-direct-browser-access header for CORS --- agrifine-extension/src/utils/api.js | 1 + 1 file changed, 1 insertion(+) diff --git a/agrifine-extension/src/utils/api.js b/agrifine-extension/src/utils/api.js index dc09b16bbb..639abdde91 100644 --- a/agrifine-extension/src/utils/api.js +++ b/agrifine-extension/src/utils/api.js @@ -47,6 +47,7 @@ export async function fetchAnthropic({ system, userMessage, maxTokens = 1024 }) 'Content-Type': 'application/json', 'x-api-key': apiKey, 'anthropic-version': '2023-06-01', + 'anthropic-dangerous-direct-browser-access': 'true', }, body: JSON.stringify(body), }); From 3185b04ed695a5629e9cdf1426d170554c7a9261 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 27 Jun 2026 04:35:32 +0000 Subject: [PATCH 06/17] fix: patch all 10 code-review findings in agrifine-extension - agent.js: add anthropic-dangerous-direct-browser-access header to _callAPI (root cause of 401 CORS errors in AgriAgent tab) - agent.js: handle stop_reason=max_tokens alongside end_turn - ag-refine/index.js: reset isRunning in a try/catch around agent.run() - tools.js: check res.ok before parsing JSON in toolLookupUSDAsoil - tools.js: guard p.name?.toLowerCase() against null field names - field-profile/index.js: guard both lat and lon before toFixed() - reading-list/index.js: escapeHtml + safeHref to prevent XSS from page titles/urls - dashboard/index.js: escapeHtml AI answer and all dynamic list content - data-ingest/index.js: add .xlsx/.xls extension fallbacks for missing MIME type - storage.js: check chrome.runtime.lastError in all four storage helpers Co-Authored-By: Claude Sonnet 4.6 Claude-Session: https://claude.ai/code/session_01KBD2dN2KEjzz3UQFa9hEpu --- agrifine-extension/src/ag-refine/agent.js | 4 +-- agrifine-extension/src/ag-refine/index.js | 10 ++++++- agrifine-extension/src/ag-refine/tools.js | 3 +- .../src/modules/dashboard/index.js | 16 +++++++---- .../src/modules/data-ingest/index.js | 5 +++- .../src/modules/field-profile/index.js | 2 +- .../src/modules/reading-list/index.js | 23 +++++++++++---- agrifine-extension/src/utils/storage.js | 28 +++++++++++++------ 8 files changed, 67 insertions(+), 24 deletions(-) diff --git a/agrifine-extension/src/ag-refine/agent.js b/agrifine-extension/src/ag-refine/agent.js index 75af4ba476..fc2f0a0f56 100644 --- a/agrifine-extension/src/ag-refine/agent.js +++ b/agrifine-extension/src/ag-refine/agent.js @@ -61,8 +61,7 @@ export class AgrifineAgent { // Append assistant turn messages.push({ role: 'assistant', content: response.content }); - if (response.stop_reason === 'end_turn') { - // Extract final text + if (response.stop_reason === 'end_turn' || response.stop_reason === 'max_tokens') { const text = response.content .filter((b) => b.type === 'text') .map((b) => b.text) @@ -113,6 +112,7 @@ export class AgrifineAgent { 'Content-Type': 'application/json', 'x-api-key': apiKey, 'anthropic-version': '2023-06-01', + 'anthropic-dangerous-direct-browser-access': 'true', }, body: JSON.stringify(body), }); diff --git a/agrifine-extension/src/ag-refine/index.js b/agrifine-extension/src/ag-refine/index.js index d870629581..a5097346ab 100644 --- a/agrifine-extension/src/ag-refine/index.js +++ b/agrifine-extension/src/ag-refine/index.js @@ -153,7 +153,15 @@ export function AgRefineModule() { }, }); - await agent.run(userText); + try { + await agent.run(userText); + } catch (err) { + const idx = messages.findIndex((m) => m.id === thinkingId); + if (idx >= 0) messages.splice(idx, 1); + messages.push({ role: 'error', text: err.message }); + isRunning = false; + this._renderMessages(container); + } }, _renderMessages(container) { diff --git a/agrifine-extension/src/ag-refine/tools.js b/agrifine-extension/src/ag-refine/tools.js index 6d693cd6b7..f47cf3a9cb 100644 --- a/agrifine-extension/src/ag-refine/tools.js +++ b/agrifine-extension/src/ag-refine/tools.js @@ -151,7 +151,7 @@ async function toolGetReadingList({ tag } = {}) { async function toolGetFieldProfiles({ field_name } = {}) { const profiles = await getFieldProfiles(); const filtered = field_name - ? profiles.filter((p) => p.name.toLowerCase().includes(field_name.toLowerCase())) + ? profiles.filter((p) => p.name?.toLowerCase().includes(field_name.toLowerCase())) : profiles; return { count: filtered.length, @@ -260,6 +260,7 @@ async function toolLookupUSDAsoil({ latitude, longitude }) { headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: `request=query&query=${encodeURIComponent(query)}&format=JSON`, }); + if (!res.ok) throw new Error(`USDA SDA API ${res.status}`); const data = await res.json(); const rows = data.Table ?? []; return { diff --git a/agrifine-extension/src/modules/dashboard/index.js b/agrifine-extension/src/modules/dashboard/index.js index 390d7db1b3..bff6de4e0d 100644 --- a/agrifine-extension/src/modules/dashboard/index.js +++ b/agrifine-extension/src/modules/dashboard/index.js @@ -3,6 +3,12 @@ import { } from '../../utils/storage.js'; import { callAnthropic } from '../../utils/api.js'; +function escapeHtml(str) { + return String(str ?? '') + .replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"'); +} + const CATEGORIES = ['all', 'land', 'equipment', 'harvest', 'finance', 'carbon', 'weather']; function tagCategory(item) { @@ -118,7 +124,7 @@ export function DashboardModule() { maxTokens: 512, }); - answerEl.innerHTML = `

Answer

${answer}`; + answerEl.innerHTML = `

Answer

${escapeHtml(answer)}`; } catch (err) { answerEl.textContent = `Error: ${err.message}`; } finally { @@ -167,11 +173,11 @@ export function DashboardModule() {
${sourceIcon}
-

${title}

- ${sub ? `

${sub}

` : ''} +

${escapeHtml(title)}

+ ${sub ? `

${escapeHtml(sub)}

` : ''}
- ${item._category} - ${(item.tags ?? []).filter((t) => t !== item._category).slice(0, 2).map((t) => `${t}`).join('')} + ${escapeHtml(item._category)} + ${(item.tags ?? []).filter((t) => t !== item._category).slice(0, 2).map((t) => `${escapeHtml(t)}`).join('')} ${date ? `${new Date(date).toLocaleDateString()}` : ''}
diff --git a/agrifine-extension/src/modules/data-ingest/index.js b/agrifine-extension/src/modules/data-ingest/index.js index dce5edbc3b..79e4117802 100644 --- a/agrifine-extension/src/modules/data-ingest/index.js +++ b/agrifine-extension/src/modules/data-ingest/index.js @@ -69,7 +69,10 @@ export function DataIngestModule() { async _processFile(file, container) { const status = container.querySelector('#ingest-status'); - const typeName = SUPPORTED_TYPES[file.type] ?? (file.name.endsWith('.csv') ? 'CSV' : null); + const typeName = SUPPORTED_TYPES[file.type] + ?? (file.name.endsWith('.csv') ? 'CSV' + : (file.name.endsWith('.xlsx') || file.name.endsWith('.xls')) ? 'Excel' + : null); if (!typeName) { status.textContent = 'Unsupported file type.'; diff --git a/agrifine-extension/src/modules/field-profile/index.js b/agrifine-extension/src/modules/field-profile/index.js index 646f650880..958b3941f6 100644 --- a/agrifine-extension/src/modules/field-profile/index.js +++ b/agrifine-extension/src/modules/field-profile/index.js @@ -148,7 +148,7 @@ export function FieldProfileModule() {
- ${p.coordinates?.lat ? `

📍 ${p.coordinates.lat.toFixed(4)}, ${p.coordinates.lon.toFixed(4)}

` : ''} + ${p.coordinates?.lat != null && p.coordinates?.lon != null ? `

📍 ${p.coordinates.lat.toFixed(4)}, ${p.coordinates.lon.toFixed(4)}

` : ''} ${p.notes ? `

📝 ${p.notes}

` : ''}

Weather data: Phase 6

Carbon potential: Phase 7

diff --git a/agrifine-extension/src/modules/reading-list/index.js b/agrifine-extension/src/modules/reading-list/index.js index 8c3db7c100..47af440c2a 100644 --- a/agrifine-extension/src/modules/reading-list/index.js +++ b/agrifine-extension/src/modules/reading-list/index.js @@ -1,6 +1,19 @@ import { getReadingList, saveReadingItem, deleteReadingItem } from '../../utils/storage.js'; import { callAnthropic, AGRICULTURE_TAGS } from '../../utils/api.js'; +function escapeHtml(str) { + return String(str ?? '') + .replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"'); +} + +function safeHref(url) { + try { + const u = new URL(url); + return (u.protocol === 'https:' || u.protocol === 'http:') ? escapeHtml(url) : '#'; + } catch (_) { return '#'; } +} + export function ReadingListModule() { let currentTag = 'all'; @@ -126,18 +139,18 @@ export function ReadingListModule() { } listEl.innerHTML = filtered.map((item) => ` -
+
- ${item.summary ? `

${item.summary}

` : ''} + ${item.summary ? `

${escapeHtml(item.summary)}

` : ''}
- ${(item.tags ?? []).map((t) => `${t}`).join('')} + ${(item.tags ?? []).map((t) => `${escapeHtml(t)}`).join('')}

${new Date(item.savedAt).toLocaleDateString()}

diff --git a/agrifine-extension/src/utils/storage.js b/agrifine-extension/src/utils/storage.js index f5f6b95e86..680a433e4f 100644 --- a/agrifine-extension/src/utils/storage.js +++ b/agrifine-extension/src/utils/storage.js @@ -22,26 +22,38 @@ export const KEYS = { // ── Generic helpers ────────────────────────────────────────────────────────── export async function localGet(key) { - return new Promise((resolve) => { - chrome.storage.local.get(key, (result) => resolve(result[key] ?? null)); + return new Promise((resolve, reject) => { + chrome.storage.local.get(key, (result) => { + if (chrome.runtime.lastError) { reject(new Error(chrome.runtime.lastError.message)); return; } + resolve(result[key] ?? null); + }); }); } export async function localSet(key, value) { - return new Promise((resolve) => { - chrome.storage.local.set({ [key]: value }, resolve); + return new Promise((resolve, reject) => { + chrome.storage.local.set({ [key]: value }, () => { + if (chrome.runtime.lastError) { reject(new Error(chrome.runtime.lastError.message)); return; } + resolve(); + }); }); } export async function sessionGet(key) { - return new Promise((resolve) => { - chrome.storage.session.get(key, (result) => resolve(result[key] ?? null)); + return new Promise((resolve, reject) => { + chrome.storage.session.get(key, (result) => { + if (chrome.runtime.lastError) { reject(new Error(chrome.runtime.lastError.message)); return; } + resolve(result[key] ?? null); + }); }); } export async function sessionSet(key, value) { - return new Promise((resolve) => { - chrome.storage.session.set({ [key]: value }, resolve); + return new Promise((resolve, reject) => { + chrome.storage.session.set({ [key]: value }, () => { + if (chrome.runtime.lastError) { reject(new Error(chrome.runtime.lastError.message)); return; } + resolve(); + }); }); } From 734664256342464c201ecf067c5151b221e24064 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 27 Jun 2026 06:08:04 +0000 Subject: [PATCH 07/17] feat: add 5 new AgriAgent tools for web browsing and data export MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit screenshot_active_tab — captures current browser tab as JPEG; agent.js formats the result as an Anthropic vision image block so Claude can actually see the page (not just its text) get_page_content — reads text of the active tab via content script, or falls back to the reading-list cache when a URL is supplied export_farm_data — generates a CSV or JSON file and triggers a browser download for reading_list, field_profiles, ingested_files, or all data open_tab — opens any https:// URL in a new browser tab and waits for it to finish loading; returns tab_id for chained tool calls read_tab_content — extracts and parses page text from any tab by tab_id (or active tab) using chrome.scripting.executeScript; more reliable than the content-script sendMessage path Background worker gains CAPTURE_SCREENSHOT, GET_ACTIVE_TAB_CONTENT, OPEN_TAB, and READ_TAB_CONTENT message handlers; agent.js detects _type:'image' results and formats them as vision content blocks. Co-Authored-By: Claude Sonnet 4.6 Claude-Session: https://claude.ai/code/session_01KBD2dN2KEjzz3UQFa9hEpu --- agrifine-extension/src/ag-refine/agent.js | 28 ++- agrifine-extension/src/ag-refine/index.js | 22 +- agrifine-extension/src/ag-refine/tools.js | 236 +++++++++++++++++++++ agrifine-extension/src/background/index.js | 94 +++++++- 4 files changed, 366 insertions(+), 14 deletions(-) diff --git a/agrifine-extension/src/ag-refine/agent.js b/agrifine-extension/src/ag-refine/agent.js index fc2f0a0f56..645122977b 100644 --- a/agrifine-extension/src/ag-refine/agent.js +++ b/agrifine-extension/src/ag-refine/agent.js @@ -86,11 +86,29 @@ export class AgrifineAgent { this.onEvent({ type: 'tool_result', data: { name: block.name, result } }); - toolResults.push({ - type: 'tool_result', - tool_use_id: block.id, - content: JSON.stringify(result), - }); + // Screenshot tool returns an image — pass it as a vision content block + if (result && result._type === 'image') { + toolResults.push({ + type: 'tool_result', + tool_use_id: block.id, + content: [ + { + type: 'image', + source: { type: 'base64', media_type: result.media_type, data: result.data }, + }, + { + type: 'text', + text: `Screenshot of "${result.title}" (${result.url})`, + }, + ], + }); + } else { + toolResults.push({ + type: 'tool_result', + tool_use_id: block.id, + content: JSON.stringify(result), + }); + } } messages.push({ role: 'user', content: toolResults }); diff --git a/agrifine-extension/src/ag-refine/index.js b/agrifine-extension/src/ag-refine/index.js index a5097346ab..4d55ea3ef2 100644 --- a/agrifine-extension/src/ag-refine/index.js +++ b/agrifine-extension/src/ag-refine/index.js @@ -1,20 +1,26 @@ import { AgrifineAgent } from './agent.js'; const TOOL_ICONS = { - get_reading_list: '📖', - get_field_profiles: '🌱', - get_ingested_files: '📄', - get_weather: '🌤️', - lookup_usda_soil: '🏛️', - calculate_gdd: '📊', + get_reading_list: '📖', + get_field_profiles: '🌱', + get_ingested_files: '📄', + get_weather: '🌤️', + lookup_usda_soil: '🏛️', + calculate_gdd: '📊', + screenshot_active_tab: '📸', + get_page_content: '🔍', + export_farm_data: '⬇️', + open_tab: '🌐', + read_tab_content: '📋', }; const SUGGESTED_PROMPTS = [ 'What are my current field conditions and harvest windows?', 'Which fields have the best soil for carbon sequestration?', 'Summarise all my farm data and flag any issues', - 'What does the 7-day weather look like for my fields?', - 'What USDA programs might I qualify for based on my fields?', + 'Screenshot this page and tell me what agricultural data you see', + 'Read this page and save any farm data you find', + 'Export my reading list and field profiles to CSV', ]; export function AgRefineModule() { diff --git a/agrifine-extension/src/ag-refine/tools.js b/agrifine-extension/src/ag-refine/tools.js index f47cf3a9cb..b24cf6ba27 100644 --- a/agrifine-extension/src/ag-refine/tools.js +++ b/agrifine-extension/src/ag-refine/tools.js @@ -1,5 +1,11 @@ import { getReadingList, getIngestedFiles, getFieldProfiles } from '../utils/storage.js'; +function csvEscape(val) { + const s = String(val ?? ''); + return (s.includes(',') || s.includes('"') || s.includes('\n')) + ? `"${s.replace(/"/g, '""')}"` : s; +} + // ── Tool definitions sent to Claude ────────────────────────────────────────── export const TOOL_DEFINITIONS = [ @@ -86,6 +92,86 @@ export const TOOL_DEFINITIONS = [ required: ['latitude', 'longitude'], }, }, + { + name: 'screenshot_active_tab', + description: 'Take a screenshot of the currently active browser tab. Returns an image Claude can visually inspect — use this to see what the user is currently viewing, check a web page layout, verify data on screen, or analyse any visible content.', + input_schema: { + type: 'object', + properties: { + description: { + type: 'string', + description: 'Optional note about why the screenshot is being taken (for context)', + }, + }, + required: [], + }, + }, + { + name: 'get_page_content', + description: 'Fetch the full text content of the currently active browser tab, or look up a saved reading-list URL. Use this to read articles, extract data from web pages, or analyse the text of any page the user has open.', + input_schema: { + type: 'object', + properties: { + url: { + type: 'string', + description: 'Optional URL to look up in the saved reading list. If omitted, reads the active tab.', + }, + }, + required: [], + }, + }, + { + name: 'export_farm_data', + description: 'Generate and download a CSV or JSON export of farm data. Triggers a file download in the user\'s browser. Use when the user asks to export, download, or save their farm data.', + input_schema: { + type: 'object', + properties: { + data_type: { + type: 'string', + enum: ['reading_list', 'field_profiles', 'ingested_files', 'all'], + description: 'Which data set to export', + }, + format: { + type: 'string', + enum: ['csv', 'json'], + description: 'File format (csv or json). "all" data_type always uses json.', + }, + }, + required: ['data_type'], + }, + }, + { + name: 'open_tab', + description: 'Open a URL in a new browser tab and wait for it to load. Use this to navigate to a relevant website — USDA, weather services, commodity markets, farm news, etc. After opening, call read_tab_content or screenshot_active_tab to extract information.', + input_schema: { + type: 'object', + properties: { + url: { + type: 'string', + description: 'The full URL to open (must start with https:// or http://)', + }, + reason: { + type: 'string', + description: 'Why you are opening this URL — shown to the user', + }, + }, + required: ['url'], + }, + }, + { + name: 'read_tab_content', + description: 'Extract and parse the text content of a browser tab. Call after open_tab to read the page that was just loaded, or omit tab_id to read the currently active tab.', + input_schema: { + type: 'object', + properties: { + tab_id: { + type: 'number', + description: 'Tab ID returned by open_tab. Omit to read the currently active tab.', + }, + }, + required: [], + }, + }, { name: 'calculate_gdd', description: 'Calculate Growing Degree Days from temperature data. Uses base temp of 50°F for forage crops.', @@ -128,6 +214,16 @@ export async function executeTool(name, input) { return toolLookupUSDAsoil(input); case 'calculate_gdd': return toolCalculateGDD(input); + case 'screenshot_active_tab': + return toolScreenshotActiveTab(input); + case 'get_page_content': + return toolGetPageContent(input); + case 'export_farm_data': + return toolExportFarmData(input); + case 'open_tab': + return toolOpenTab(input); + case 'read_tab_content': + return toolReadTabContent(input); default: return { error: `Unknown tool: ${name}` }; } @@ -284,6 +380,146 @@ async function toolLookupUSDAsoil({ latitude, longitude }) { } } +function toolScreenshotActiveTab() { + return new Promise((resolve, reject) => { + chrome.runtime.sendMessage({ type: 'CAPTURE_SCREENSHOT' }, (response) => { + if (chrome.runtime.lastError) { + reject(new Error(chrome.runtime.lastError.message)); + return; + } + if (response?.error) { + reject(new Error(response.error)); + return; + } + // Strip data URL prefix — agent.js will format this as an image content block + const base64 = response.dataUrl.replace(/^data:image\/\w+;base64,/, ''); + resolve({ + _type: 'image', + media_type: 'image/jpeg', + data: base64, + url: response.url, + title: response.title, + }); + }); + }); +} + +async function toolGetPageContent({ url } = {}) { + // Check reading list cache first if a URL was given + if (url) { + const list = await getReadingList(); + const saved = list.find((i) => i.url === url || i.url.startsWith(url)); + if (saved) { + return { + url: saved.url, + title: saved.title, + summary: saved.summary, + tags: saved.tags, + source: 'reading_list_cache', + }; + } + } + + // Fall back to reading the active tab via content script + return new Promise((resolve, reject) => { + chrome.runtime.sendMessage({ type: 'GET_ACTIVE_TAB_CONTENT' }, (response) => { + if (chrome.runtime.lastError) { + reject(new Error(chrome.runtime.lastError.message)); + return; + } + if (response?.error) { + reject(new Error(response.error)); + return; + } + resolve(response); + }); + }); +} + +async function toolExportFarmData({ data_type, format = 'csv' }) { + let records; + let filename; + let content; + const date = new Date().toISOString().slice(0, 10); + + if (data_type === 'all') { + const [rl, files, profiles] = await Promise.all([getReadingList(), getIngestedFiles(), getFieldProfiles()]); + filename = `agrifine_export_${date}.json`; + content = JSON.stringify({ reading_list: rl, ingested_files: files, field_profiles: profiles }, null, 2); + records = rl.length + files.length + profiles.length; + } else if (data_type === 'reading_list') { + const list = await getReadingList(); + records = list.length; + filename = `agrifine_reading_list_${date}.${format}`; + if (format === 'json') { + content = JSON.stringify(list, null, 2); + } else { + const hdrs = ['title', 'url', 'summary', 'tags', 'savedAt']; + const rows = list.map((i) => [i.title, i.url, i.summary ?? '', (i.tags ?? []).join('; '), i.savedAt].map(csvEscape)); + content = [hdrs.join(','), ...rows.map((r) => r.join(','))].join('\n'); + } + } else if (data_type === 'field_profiles') { + const profiles = await getFieldProfiles(); + records = profiles.length; + filename = `agrifine_field_profiles_${date}.${format}`; + if (format === 'json') { + content = JSON.stringify(profiles, null, 2); + } else { + const hdrs = ['name', 'acres', 'soil_type', 'latitude', 'longitude', 'clu_id', 'notes', 'created_at']; + const rows = profiles.map((p) => [ + p.name, p.acres ?? '', p.soilType ?? '', + p.coordinates?.lat ?? '', p.coordinates?.lon ?? '', + p.cluId ?? '', p.notes ?? '', p.createdAt, + ].map(csvEscape)); + content = [hdrs.join(','), ...rows.map((r) => r.join(','))].join('\n'); + } + } else if (data_type === 'ingested_files') { + const files = await getIngestedFiles(); + records = files.length; + filename = `agrifine_ingested_files_${date}.json`; + content = JSON.stringify(files, null, 2); + } else { + return { error: `Unknown data_type: ${data_type}` }; + } + + // Trigger download inside the sidebar page + const mimeType = filename.endsWith('.json') ? 'application/json' : 'text/csv'; + const blob = new Blob([content], { type: mimeType }); + const objectUrl = URL.createObjectURL(blob); + const anchor = document.createElement('a'); + anchor.href = objectUrl; + anchor.download = filename; + document.body.appendChild(anchor); + anchor.click(); + document.body.removeChild(anchor); + setTimeout(() => URL.revokeObjectURL(objectUrl), 2000); + + return { exported: true, filename, record_count: records, format: filename.split('.').pop(), data_type }; +} + +function toolOpenTab({ url, reason }) { + if (!url.startsWith('http://') && !url.startsWith('https://')) { + return Promise.resolve({ error: 'URL must start with http:// or https://' }); + } + return new Promise((resolve, reject) => { + chrome.runtime.sendMessage({ type: 'OPEN_TAB', payload: { url } }, (response) => { + if (chrome.runtime.lastError) { reject(new Error(chrome.runtime.lastError.message)); return; } + if (response?.error) { reject(new Error(response.error)); return; } + resolve({ ...response, reason: reason ?? null }); + }); + }); +} + +function toolReadTabContent({ tab_id } = {}) { + return new Promise((resolve, reject) => { + chrome.runtime.sendMessage({ type: 'READ_TAB_CONTENT', payload: { tab_id: tab_id ?? null } }, (response) => { + if (chrome.runtime.lastError) { reject(new Error(chrome.runtime.lastError.message)); return; } + if (response?.error) { reject(new Error(response.error)); return; } + resolve(response); + }); + }); +} + function toolCalculateGDD({ daily_highs, daily_lows, base_temp = 50 }) { const gdd_per_day = daily_highs.map((hi, i) => { const lo = daily_lows[i] ?? hi; diff --git a/agrifine-extension/src/background/index.js b/agrifine-extension/src/background/index.js index 76e83d262e..60f0b0d318 100644 --- a/agrifine-extension/src/background/index.js +++ b/agrifine-extension/src/background/index.js @@ -20,10 +20,33 @@ chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => { return true; case 'GET_PAGE_CONTENT': - // Content script relays page text; background stores it temporarily sendResponse({ ok: true }); return false; + case 'CAPTURE_SCREENSHOT': + captureActiveTabScreenshot() + .then(sendResponse) + .catch((err) => sendResponse({ error: err.message })); + return true; + + case 'GET_ACTIVE_TAB_CONTENT': + getActiveTabContent() + .then(sendResponse) + .catch((err) => sendResponse({ error: err.message })); + return true; + + case 'OPEN_TAB': + openUrlInTab(message.payload.url) + .then(sendResponse) + .catch((err) => sendResponse({ error: err.message })); + return true; + + case 'READ_TAB_CONTENT': + readTabContent(message.payload?.tab_id) + .then(sendResponse) + .catch((err) => sendResponse({ error: err.message })); + return true; + default: return false; } @@ -34,6 +57,75 @@ async function handleAnthropicRequest({ system, userMessage, maxTokens }) { return { text }; } +async function captureActiveTabScreenshot() { + const [tab] = await chrome.tabs.query({ active: true, currentWindow: true }); + if (!tab) throw new Error('No active tab found'); + const dataUrl = await chrome.tabs.captureVisibleTab(tab.windowId, { format: 'jpeg', quality: 80 }); + return { dataUrl, url: tab.url, title: tab.title }; +} + +async function getActiveTabContent() { + const [tab] = await chrome.tabs.query({ active: true, currentWindow: true }); + if (!tab) throw new Error('No active tab found'); + try { + const resp = await chrome.tabs.sendMessage(tab.id, { type: 'GET_PAGE_INFO' }); + return { url: tab.url, title: tab.title, text: resp?.text ?? '', source: 'active_tab' }; + } catch (_) { + return { + url: tab.url, + title: tab.title, + text: '', + source: 'active_tab', + note: 'Content script unavailable on this page (chrome://, extensions, etc.)', + }; + } +} + +function waitForTabLoad(tabId, timeoutMs = 20000) { + return new Promise((resolve, reject) => { + const timer = setTimeout(() => { + chrome.tabs.onUpdated.removeListener(listener); + chrome.tabs.get(tabId).then(resolve).catch(() => reject(new Error('Tab load timed out'))); + }, timeoutMs); + function listener(id, info, tab) { + if (id !== tabId || info.status !== 'complete') return; + chrome.tabs.onUpdated.removeListener(listener); + clearTimeout(timer); + resolve(tab); + } + chrome.tabs.onUpdated.addListener(listener); + }); +} + +async function openUrlInTab(url) { + const tab = await chrome.tabs.create({ url, active: true }); + const loaded = await waitForTabLoad(tab.id); + return { tab_id: loaded.id, url: loaded.url, title: loaded.title, status: 'ready' }; +} + +async function readTabContent(tabId) { + const targetId = tabId + ?? (await chrome.tabs.query({ active: true, currentWindow: true }))[0]?.id; + if (!targetId) throw new Error('No tab found'); + + const [result] = await chrome.scripting.executeScript({ + target: { tabId: targetId }, + func: () => { + const selectors = ['article', 'main', '[role="main"]', '.content', '#content']; + for (const sel of selectors) { + const el = document.querySelector(sel); + if (el) { + const clone = el.cloneNode(true); + clone.querySelectorAll('script,style,nav,header,footer,aside').forEach((n) => n.remove()); + return { url: location.href, title: document.title, text: clone.innerText.trim().slice(0, 8000) }; + } + } + return { url: location.href, title: document.title, text: document.body?.innerText?.slice(0, 8000) ?? '' }; + }, + }); + return result.result; +} + // Keep service worker alive during active side-panel sessions chrome.runtime.onConnect.addListener((port) => { if (port.name === 'keepalive') { From 05a3a3bbd0d51e9fd28d72ca828e5875f15b84ce Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 27 Jun 2026 06:12:05 +0000 Subject: [PATCH 08/17] feat: persistent farm memory and enriched context bundle for AgriAgent MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Farm memory (agrifine_farm_memory in chrome.storage.local): - New FarmMemory schema: aiGeneratedSummary, farm_name, total_acres, primary_crops, soil_overview, key_insights, action_items, risk_flags, opportunities, lastUpdated - getFarmMemory() / saveFarmMemory() in storage.js buildContextBundle() now loads all four data sources in parallel: 1. Farm memory snapshot (AI synthesis from prior sessions) — at top 2. Field profiles with crop history, harvest records, and coordinates 3. Ingested data files with structured-data previews 4. Reading list articles with summaries and tags Two new AgriAgent tools: - get_farm_memory: retrieve the stored knowledge snapshot - update_farm_memory: agent saves a comprehensive farm synthesis so future sessions start with full context (the key to persistent memory) System prompt rewrite in agent.js: - Agent now understands its role as the farm's persistent advisor - Memory protocol: reference farm memory first, update it when new insights are discovered - Explicit reasoning steps: Ground → Gaps → Connect → Cite → Remember - Full tool selection guide with when-to-use guidance for all 11 tools Co-Authored-By: Claude Sonnet 4.6 Claude-Session: https://claude.ai/code/session_01KBD2dN2KEjzz3UQFa9hEpu --- agrifine-extension/src/ag-refine/agent.js | 46 ++++++-- agrifine-extension/src/ag-refine/index.js | 7 +- agrifine-extension/src/ag-refine/tools.js | 59 ++++++++++- agrifine-extension/src/utils/storage.js | 123 +++++++++++++++++++--- 4 files changed, 204 insertions(+), 31 deletions(-) diff --git a/agrifine-extension/src/ag-refine/agent.js b/agrifine-extension/src/ag-refine/agent.js index 645122977b..e3aa914a93 100644 --- a/agrifine-extension/src/ag-refine/agent.js +++ b/agrifine-extension/src/ag-refine/agent.js @@ -26,16 +26,42 @@ export class AgrifineAgent { const contextBundle = await buildContextBundle(); - const systemPrompt = [ - 'You are AgriAgent, an expert AI assistant for farm operations management.', - 'You have access to the user\'s farm data through tools — always use them before answering.', - 'When answering questions about fields, weather, yields, or finances: first query the relevant data, then synthesize a clear answer.', - 'Be specific: cite field names, dates, acreage, and numbers from the actual data.', - 'For weather queries on a field, always look up the field profile first to get coordinates.', - '', - 'FARM CONTEXT (reading list summaries + field profiles):', - contextBundle, - ].join('\n'); + const systemPrompt = `You are AgriAgent, the dedicated AI advisor for this farm operation. You maintain a persistent "farm memory" — a synthesized knowledge base you build and update over time as you learn more about the operation. + +IDENTITY AND ROLE +- You are this farm's trusted advisor with deep, specific knowledge of its fields, crops, soils, finances, and operations. +- You think both tactically (today's weather, this week's harvest window) and strategically (long-term soil health, carbon sequestration, USDA program eligibility). +- Your answers are always grounded in the farm's actual data — never guess or use generic advice when specific data is available. + +MEMORY PROTOCOL +- The FARM CONTEXT section below is pre-loaded with all available data from every source, including your previously stored farm memory. +- The farm memory (if present) is the most important section — it is your synthesized understanding of this operation built from prior analysis. Reference it first. +- When you discover something significant (a pattern, risk, or opportunity not already captured), call update_farm_memory to preserve it for future sessions. +- If farm memory is absent or stale (>14 days old), proactively synthesize one after reviewing the available field and file data. + +REASONING APPROACH — follow this order: +1. GROUND: What does the farm memory and pre-loaded context already tell me? +2. GAPS: What additional data do I need? Use the right tool — don't query what's already in context. +3. CONNECT: Link data across sources (e.g. soil type + weather + crop history → harvest recommendation). +4. CITE: Always name fields, dates, acreages, and numbers from the actual data. +5. REMEMBER: Did this conversation reveal anything new? If so, update_farm_memory. + +TOOL SELECTION GUIDE +- get_field_profiles — field locations, soil type, acreage, crop history, harvest records +- get_weather(lat, lon) — live 7-day forecast + GDD; always get field coordinates first +- lookup_usda_soil(lat, lon) — USDA soil classification and organic matter data +- get_ingested_files — uploaded CSVs, Excel files, PDFs with extracted structured data +- get_reading_list — saved articles, research, USDA notices +- calculate_gdd(highs, lows) — growing degree day accumulation from temperature data +- screenshot_active_tab — capture the current browser page as an image you can see +- get_page_content — read text from the active tab or a saved reading-list URL +- open_tab(url) + read_tab_content(tab_id) — navigate to a URL and parse its content +- export_farm_data — generate and download CSV/JSON of farm data +- get_farm_memory — retrieve the stored farm knowledge snapshot +- update_farm_memory — save new insights about this farm for future sessions + +FARM CONTEXT (all data sources pre-loaded — memory, field profiles, ingested files, reading list): +${contextBundle}`; const messages = [{ role: 'user', content: userMessage }]; diff --git a/agrifine-extension/src/ag-refine/index.js b/agrifine-extension/src/ag-refine/index.js index 4d55ea3ef2..15f405b845 100644 --- a/agrifine-extension/src/ag-refine/index.js +++ b/agrifine-extension/src/ag-refine/index.js @@ -12,14 +12,15 @@ const TOOL_ICONS = { export_farm_data: '⬇️', open_tab: '🌐', read_tab_content: '📋', + get_farm_memory: '🧠', + update_farm_memory: '💾', }; const SUGGESTED_PROMPTS = [ + 'Review all my farm data and build a farm memory summary', 'What are my current field conditions and harvest windows?', - 'Which fields have the best soil for carbon sequestration?', - 'Summarise all my farm data and flag any issues', + 'What risks or opportunities do you see across my operation?', 'Screenshot this page and tell me what agricultural data you see', - 'Read this page and save any farm data you find', 'Export my reading list and field profiles to CSV', ]; diff --git a/agrifine-extension/src/ag-refine/tools.js b/agrifine-extension/src/ag-refine/tools.js index b24cf6ba27..0fe1fde2d8 100644 --- a/agrifine-extension/src/ag-refine/tools.js +++ b/agrifine-extension/src/ag-refine/tools.js @@ -1,4 +1,4 @@ -import { getReadingList, getIngestedFiles, getFieldProfiles } from '../utils/storage.js'; +import { getReadingList, getIngestedFiles, getFieldProfiles, getFarmMemory, saveFarmMemory } from '../utils/storage.js'; function csvEscape(val) { const s = String(val ?? ''); @@ -140,6 +140,33 @@ export const TOOL_DEFINITIONS = [ required: ['data_type'], }, }, + { + name: 'get_farm_memory', + description: 'Retrieve the stored farm memory — the AI-synthesized knowledge base for this operation. Returns the most recent summary, key insights, action items, risk flags, and opportunities identified in prior sessions. Call this if the system context did not already include farm memory.', + input_schema: { type: 'object', properties: {}, required: [] }, + }, + { + name: 'update_farm_memory', + description: 'Save an updated farm memory snapshot. Call this after synthesizing new insights so future sessions benefit from what you learned. Write a comprehensive aiGeneratedSummary covering the whole farm operation — fields, soils, crops, patterns, and strategic outlook.', + input_schema: { + type: 'object', + properties: { + aiGeneratedSummary: { + type: 'string', + description: 'A rich narrative synthesis of the farm operation. Cover: total acreage, each field\'s status, soil conditions, crop history patterns, financial health (if data available), key risks, and strategic opportunities. Write as a briefing you\'d give a new advisor.', + }, + farm_name: { type: 'string', description: 'Farm or operation name if known' }, + total_acres: { type: 'number', description: 'Total acreage across all fields' }, + primary_crops: { type: 'array', items: { type: 'string' }, description: 'Primary crops grown' }, + soil_overview: { type: 'string', description: 'Summary of soil conditions across the operation' }, + key_insights: { type: 'array', items: { type: 'string' }, description: 'Most important observations — patterns, correlations, or findings about this farm' }, + action_items: { type: 'array', items: { type: 'string' }, description: 'Recommended next steps for the operator' }, + risk_flags: { type: 'array', items: { type: 'string' }, description: 'Risks, concerns, or issues to monitor' }, + opportunities: { type: 'array', items: { type: 'string' }, description: 'Opportunities identified — programs, practices, markets' }, + }, + required: ['aiGeneratedSummary'], + }, + }, { name: 'open_tab', description: 'Open a URL in a new browser tab and wait for it to load. Use this to navigate to a relevant website — USDA, weather services, commodity markets, farm news, etc. After opening, call read_tab_content or screenshot_active_tab to extract information.', @@ -220,6 +247,10 @@ export async function executeTool(name, input) { return toolGetPageContent(input); case 'export_farm_data': return toolExportFarmData(input); + case 'get_farm_memory': + return toolGetFarmMemory(); + case 'update_farm_memory': + return toolUpdateFarmMemory(input); case 'open_tab': return toolOpenTab(input); case 'read_tab_content': @@ -497,6 +528,32 @@ async function toolExportFarmData({ data_type, format = 'csv' }) { return { exported: true, filename, record_count: records, format: filename.split('.').pop(), data_type }; } +async function toolGetFarmMemory() { + const memory = await getFarmMemory(); + if (!memory) { + return { + has_memory: false, + message: 'No farm memory stored yet. Review the field profiles and ingested files, then call update_farm_memory to create a persistent knowledge base for this farm.', + }; + } + return { has_memory: true, ...memory }; +} + +async function toolUpdateFarmMemory(input) { + await saveFarmMemory({ + aiGeneratedSummary: input.aiGeneratedSummary, + farm_name: input.farm_name ?? null, + total_acres: input.total_acres ?? null, + primary_crops: input.primary_crops ?? [], + soil_overview: input.soil_overview ?? null, + key_insights: input.key_insights ?? [], + action_items: input.action_items ?? [], + risk_flags: input.risk_flags ?? [], + opportunities: input.opportunities ?? [], + }); + return { saved: true, message: 'Farm memory updated. Future sessions will begin with this knowledge.' }; +} + function toolOpenTab({ url, reason }) { if (!url.startsWith('http://') && !url.startsWith('https://')) { return Promise.resolve({ error: 'URL must start with http:// or https://' }); diff --git a/agrifine-extension/src/utils/storage.js b/agrifine-extension/src/utils/storage.js index 680a433e4f..abfe4b1285 100644 --- a/agrifine-extension/src/utils/storage.js +++ b/agrifine-extension/src/utils/storage.js @@ -6,17 +6,33 @@ * agrifine_ingested_files — Array * agrifine_field_profiles — Array * agrifine_settings — Settings + * agrifine_farm_memory — FarmMemory (AI-synthesized knowledge base) * * chrome.storage.session keys: * agrifine_api_key — string (never persisted to local) + * + * FarmMemory shape: + * { + * lastUpdated: ISO string, + * aiGeneratedSummary: string, // Claude's narrative synthesis of the whole farm + * farm_name: string | null, + * total_acres: number | null, + * primary_crops: string[], + * soil_overview: string | null, + * key_insights: string[], // important observations + * action_items: string[], // recommended next steps + * risk_flags: string[], // risks or concerns to watch + * opportunities: string[], // identified opportunities + * } */ export const KEYS = { - READING_LIST: 'agrifine_reading_list', - INGESTED_FILES: 'agrifine_ingested_files', - FIELD_PROFILES: 'agrifine_field_profiles', - SETTINGS: 'agrifine_settings', - API_KEY: 'agrifine_api_key', // session only + READING_LIST: 'agrifine_reading_list', + INGESTED_FILES: 'agrifine_ingested_files', + FIELD_PROFILES: 'agrifine_field_profiles', + SETTINGS: 'agrifine_settings', + FARM_MEMORY: 'agrifine_farm_memory', + API_KEY: 'agrifine_api_key', // session only }; // ── Generic helpers ────────────────────────────────────────────────────────── @@ -113,6 +129,16 @@ export async function deleteFieldProfile(id) { await localSet(KEYS.FIELD_PROFILES, profiles.filter((p) => p.id !== id)); } +// ── Farm Memory ────────────────────────────────────────────────────────────── + +export async function getFarmMemory() { + return (await localGet(KEYS.FARM_MEMORY)) ?? null; +} + +export async function saveFarmMemory(memory) { + await localSet(KEYS.FARM_MEMORY, { ...memory, lastUpdated: new Date().toISOString() }); +} + // ── Settings ───────────────────────────────────────────────────────────────── export async function getSettings() { @@ -127,22 +153,85 @@ export async function saveSettings(patch) { // ── Context bundle (used as AI system context) ─────────────────────────────── export async function buildContextBundle() { - const list = await getReadingList(); - const profiles = await getFieldProfiles(); + const [list, profiles, files, memory] = await Promise.all([ + getReadingList(), + getFieldProfiles(), + getIngestedFiles(), + getFarmMemory(), + ]); + + // ── 1. Farm memory (AI-synthesized knowledge — most important, goes first) ── + let memorySection; + if (memory) { + const lines = [ + `FARM MEMORY (last updated ${memory.lastUpdated?.slice(0, 10) ?? 'unknown'}):`, + memory.aiGeneratedSummary ?? '', + ]; + if (memory.primary_crops?.length) lines.push(`Primary crops: ${memory.primary_crops.join(', ')}`); + if (memory.total_acres != null) lines.push(`Total acreage: ${memory.total_acres} ac`); + if (memory.key_insights?.length) { + lines.push('Key insights:'); + memory.key_insights.forEach((s) => lines.push(` • ${s}`)); + } + if (memory.action_items?.length) { + lines.push('Action items:'); + memory.action_items.forEach((s) => lines.push(` • ${s}`)); + } + if (memory.risk_flags?.length) { + lines.push('Risk flags:'); + memory.risk_flags.forEach((s) => lines.push(` ⚠ ${s}`)); + } + memorySection = lines.filter(Boolean).join('\n'); + } else { + memorySection = 'FARM MEMORY: (none yet — after reviewing field data, call update_farm_memory to build a persistent knowledge base)'; + } - const readingCtx = list.slice(0, 20).map((item) => - `[${item.tags?.join(', ') ?? 'general'}] ${item.title}: ${item.summary ?? ''}` - ).join('\n'); + // ── 2. Field profiles with crop history and harvest records ────────────────── + const fieldLines = profiles.length === 0 ? ['(none)'] : profiles.map((p) => { + const coords = p.coordinates?.lat != null && p.coordinates?.lon != null + ? `${p.coordinates.lat.toFixed(4)}, ${p.coordinates.lon.toFixed(4)}` + : null; + const history = (p.cropHistory ?? []).slice(0, 4).map((h) => `${h.year}: ${h.crop}`).join(', '); + const harvests = (p.harvestRecords ?? []).slice(0, 3) + .map((h) => `${h.date?.slice(0, 10) ?? '?'}: ${h.yield} ${h.unit ?? ''}`.trim()).join('; '); + const parts = [ + `Field "${p.name}" | ${p.acres ?? '?'} ac | ${p.soilType ?? 'unknown soil'}`, + coords ? ` Coords: ${coords}` : null, + p.cluId ? ` CLU: ${p.cluId}` : null, + history ? ` Crop history: ${history}` : null, + harvests ? ` Harvests: ${harvests}` : null, + p.notes ? ` Notes: ${p.notes}` : null, + ]; + return parts.filter(Boolean).join('\n'); + }); - const fieldCtx = profiles.map((p) => - `Field "${p.name}" (${p.acres ?? '?'} ac, ${p.soilType ?? 'unknown soil'}): ${p.notes ?? ''}` - ).join('\n'); + // ── 3. Ingested data files ─────────────────────────────────────────────────── + const fileLines = files.length === 0 ? ['(none)'] : files.slice(0, 10).map((f) => { + const preview = f.structuredData + ? Object.entries(f.structuredData) + .filter(([k]) => k !== 'raw_preview' && k !== 'parse_error') + .slice(0, 5) + .map(([k, v]) => `${k}: ${JSON.stringify(v).slice(0, 120)}`) + .join(' | ') + : f.preview?.slice(0, 200) ?? '(no structured data)'; + return `[${f.type}] ${f.filename} (${f.uploadedAt?.slice(0, 10) ?? '?'}): ${preview}`; + }); + + // ── 4. Reading list (recent saved articles) ────────────────────────────────── + const readingLines = list.length === 0 ? ['(none)'] : list.slice(0, 15).map((item) => + `[${item.tags?.join(', ') ?? 'general'}] "${item.title}": ${item.summary ?? '(no summary)'}` + ); return [ - 'USER READING LIST CONTEXT:', - readingCtx || '(none)', + memorySection, + '', + '── FIELD PROFILES ──', + fieldLines.join('\n\n'), + '', + '── INGESTED DATA FILES ──', + fileLines.join('\n'), '', - 'FIELD PROFILES:', - fieldCtx || '(none)', + '── READING LIST (recent articles) ──', + readingLines.join('\n'), ].join('\n'); } From 564ec1145e25599375c666642e330fc71e175707 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 27 Jun 2026 06:28:50 +0000 Subject: [PATCH 09/17] Redesign Agrifine extension UI to match AG-Refine dark theme MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace all light-mode Tailwind classes across every module with the AG-Refine dark design system: #0f1621 body, #131c2b header/nav, #1a2438 cards, #1e2d40 borders, agri-green (22c55e/16a34a) accents. - tailwind.config.js: add `night` dark palette (950–300) - sidebar.css: full dark rewrite (cards, tags, inputs, scrollbar) - sidebar.html: AG/REFINE/FIELD INTELLIGENCE logo, dark tab bar with INTEL/INGEST/FIELDS/DATA/CARBON/AGENT labels and dot indicators - sidebar/index.js: manage inactive tab color via inline style - reading-list: card links → agri-400, summary/date → gray-400/500 - data-ingest: drop zone → night borders, file cards → dark theme - field-profile: form → night-700 bg, inputs use .ag-input, cards dark - dashboard: AI query bar and category pills → dark, answer box dark - carbon-estimator: feature list and notify banner → dark palette - ag-refine: suggestions → night-700, chat bubbles dark, tool call cards use night-700/border-night-600, errors use red-900/20 tint Co-Authored-By: Claude Sonnet 4.6 Claude-Session: https://claude.ai/code/session_01KBD2dN2KEjzz3UQFa9hEpu --- agrifine-extension/src/ag-refine/index.js | 32 +++---- .../src/modules/carbon-estimator/index.js | 20 ++--- .../src/modules/dashboard/index.js | 20 ++--- .../src/modules/data-ingest/index.js | 24 +++--- .../src/modules/field-profile/index.js | 46 +++++----- .../src/modules/reading-list/index.js | 14 ++-- agrifine-extension/src/sidebar/index.js | 4 +- agrifine-extension/src/sidebar/sidebar.css | 57 ++++++++----- agrifine-extension/src/sidebar/sidebar.html | 84 +++++++++++-------- agrifine-extension/tailwind.config.js | 10 +++ 10 files changed, 169 insertions(+), 142 deletions(-) diff --git a/agrifine-extension/src/ag-refine/index.js b/agrifine-extension/src/ag-refine/index.js index 15f405b845..718eb07d4e 100644 --- a/agrifine-extension/src/ag-refine/index.js +++ b/agrifine-extension/src/ag-refine/index.js @@ -40,10 +40,10 @@ export function AgRefineModule() {
🤖 -

AgriAgent

- AI Agent +

AgriAgent

+ AI Agent
-

Multi-step reasoning over all your farm data

+

Multi-step reasoning over all your farm data

@@ -51,29 +51,29 @@ export function AgRefineModule() {
-

Try asking…

+

Try asking…

${SUGGESTED_PROMPTS.map((p) => ` - `).join('')}
-
+
+ class="ag-input flex-1 rounded-xl resize-none">
- +
`; @@ -197,28 +197,28 @@ export function AgRefineModule() {
${steps.map((step) => { if (step.type === 'status') { - return `
+ return `
${escapeHtml(step.text)}
`; } if (step.type === 'tool') { - return `
+ return `
${step.icon} - ${step.name} + ${step.name} ${step.done ? '' : ''}
`; } return ''; }).join('')} - ${steps.length === 0 ? '
Starting…
' : ''} + ${steps.length === 0 ? '
Starting…
' : ''}
`; } if (msg.role === 'assistant') { return `
-
🤖
-
+
🤖
+
${escapeHtml(msg.text)}
`; @@ -226,7 +226,7 @@ export function AgRefineModule() { if (msg.role === 'error') { return ` -
+
⚠️ ${escapeHtml(msg.text)}
`; } diff --git a/agrifine-extension/src/modules/carbon-estimator/index.js b/agrifine-extension/src/modules/carbon-estimator/index.js index 097e456a76..afdcc8eeea 100644 --- a/agrifine-extension/src/modules/carbon-estimator/index.js +++ b/agrifine-extension/src/modules/carbon-estimator/index.js @@ -12,15 +12,15 @@ export function CarbonEstimatorModule() {
-
+
-
🌿
+
🌿
-

Carbon Estimator

+

Carbon Estimator

Coming in Phase 7
-

+

The Carbon Estimator will calculate your operation's Scope 3 emissions profile and estimate carbon sequestration potential per field using USDA emission factors.

@@ -35,19 +35,19 @@ export function CarbonEstimatorModule() { ['📄', 'Carbon Credit PDF', 'Downloadable eligibility summary for carbon marketplaces'], ['📡', 'Marketplace Handoff', 'Send your credit profile to Nori, Pachama, or others (Phase 8)'], ].map(([icon, title, desc]) => ` -
+
${icon}
-

${title}

-

${desc}

+

${title}

+

${desc}

`).join('')}
-
-

Your field profile data is already being collected.

-

Carbon estimates will populate automatically when Phase 7 lands.

+
+

Your field profile data is already being collected.

+

Carbon estimates will populate automatically when Phase 7 lands.

`; diff --git a/agrifine-extension/src/modules/dashboard/index.js b/agrifine-extension/src/modules/dashboard/index.js index bff6de4e0d..566f840381 100644 --- a/agrifine-extension/src/modules/dashboard/index.js +++ b/agrifine-extension/src/modules/dashboard/index.js @@ -41,13 +41,13 @@ export function DashboardModule() {
+ class="ag-input flex-1 rounded-xl" />
- +
@@ -55,12 +55,12 @@ export function DashboardModule() {
${CATEGORIES.map((c) => ` `).join('')}
+ class="ag-input" />
@@ -76,7 +76,7 @@ export function DashboardModule() { btn.addEventListener('click', async () => { activeCategory = btn.dataset.cat; container.querySelectorAll('.cat-btn').forEach((b) => { - b.className = `cat-btn text-xs px-2.5 py-1 rounded-full border transition border-gray-300 text-gray-600 hover:border-agri-400`; + b.className = `cat-btn text-xs px-2.5 py-1 rounded-full border transition border-night-500 text-gray-400 hover:border-agri-500`; }); btn.className = `cat-btn text-xs px-2.5 py-1 rounded-full border transition bg-agri-600 text-white border-agri-600`; await this._renderDashboard(container); @@ -124,7 +124,7 @@ export function DashboardModule() { maxTokens: 512, }); - answerEl.innerHTML = `

Answer

${escapeHtml(answer)}`; + answerEl.innerHTML = `

Answer

${escapeHtml(answer)}`; } catch (err) { answerEl.textContent = `Error: ${err.message}`; } finally { @@ -173,12 +173,12 @@ export function DashboardModule() {
${sourceIcon}
-

${escapeHtml(title)}

- ${sub ? `

${escapeHtml(sub)}

` : ''} +

${escapeHtml(title)}

+ ${sub ? `

${escapeHtml(sub)}

` : ''}
- ${escapeHtml(item._category)} + ${escapeHtml(item._category)} ${(item.tags ?? []).filter((t) => t !== item._category).slice(0, 2).map((t) => `${escapeHtml(t)}`).join('')} - ${date ? `${new Date(date).toLocaleDateString()}` : ''} + ${date ? `${new Date(date).toLocaleDateString()}` : ''}
diff --git a/agrifine-extension/src/modules/data-ingest/index.js b/agrifine-extension/src/modules/data-ingest/index.js index 79e4117802..2d989f6284 100644 --- a/agrifine-extension/src/modules/data-ingest/index.js +++ b/agrifine-extension/src/modules/data-ingest/index.js @@ -20,16 +20,16 @@ export function DataIngestModule() {
+ class="border-2 border-dashed border-night-500 rounded-xl p-6 text-center cursor-pointer hover:border-agri-500 hover:bg-night-800 transition"> -

Drop CSV, Excel, or PDF here

-

or click to select a file

+

Drop CSV, Excel, or PDF here

+

or click to select a file

-
+
@@ -48,16 +48,16 @@ export function DataIngestModule() { dropZone.addEventListener('dragover', (e) => { e.preventDefault(); - dropZone.classList.add('border-agri-600', 'bg-agri-50'); + dropZone.classList.add('border-agri-500', 'bg-night-800'); }); dropZone.addEventListener('dragleave', () => { - dropZone.classList.remove('border-agri-600', 'bg-agri-50'); + dropZone.classList.remove('border-agri-500', 'bg-night-800'); }); dropZone.addEventListener('drop', (e) => { e.preventDefault(); - dropZone.classList.remove('border-agri-600', 'bg-agri-50'); + dropZone.classList.remove('border-agri-500', 'bg-night-800'); const file = e.dataTransfer.files[0]; if (file) this._processFile(file, container); }); @@ -212,17 +212,17 @@ export function DataIngestModule() {
- ${f.type} -

${f.filename}

-

${new Date(f.uploadedAt).toLocaleDateString()}

+ ${f.type} +

${f.filename}

+

${new Date(f.uploadedAt).toLocaleDateString()}

-
- ${f.preview ? `
${f.preview}
` : ''} + ${f.preview ? `
${f.preview}
` : ''}
`).join(''); diff --git a/agrifine-extension/src/modules/field-profile/index.js b/agrifine-extension/src/modules/field-profile/index.js index 958b3941f6..c4f58c14db 100644 --- a/agrifine-extension/src/modules/field-profile/index.js +++ b/agrifine-extension/src/modules/field-profile/index.js @@ -23,27 +23,21 @@ export function FieldProfileModule() {
- `; return; } - listEl.innerHTML = profiles.map((p) => ` -
-
-
-

${p.name}

-
- ${p.acres ? `${p.acres} ac` : ''} - ${p.soilType ? `${p.soilType}` : ''} - ${p.cluId ? `CLU ${p.cluId}` : ''} + listEl.innerHTML = profiles.map((p) => { + const isExpanded = expandedId === p.id; + const sourceLabel = p._source === 'ag-refine' ? 'AG-Refine' + : p._source === 'ag-refine-merged' ? 'AG-Refine + manual' + : p._source === 'manual' ? 'manual' + : null; + + const cropHistoryHtml = (p.cropHistory ?? []).length > 0 + ? `
+

Crop History

+
+ ${(p.cropHistory ?? []).slice(0, 5).map((h) => ` +
+ ${h.year} — ${h.crop} + ${h.yield != null ? `${h.yield} ${h.unit ?? 'bu/ac'}` : ''} +
+ `).join('')}
-
-
-
` + : ''; + + const harvestHtml = (p.harvestRecords ?? []).length > 0 + ? `
+

Harvest Records

+
+ ${(p.harvestRecords ?? []).slice(0, 4).map((h) => ` +
+ ${h.date?.slice(0, 10) ?? '?'} — ${h.crop} + + ${h.yield != null ? `${h.yield} ${h.unit ?? ''}` : ''} + ${h.moisture != null ? ` @ ${h.moisture}%` : ''} + +
+ `).join('')} +
+
` + : ''; + + return ` +
+
+
+

${p.name}

+
+ ${p.acres != null ? `${p.acres} ac` : ''} + ${p.soilType ? `${p.soilType}` : ''} + ${p.cluId ? `CLU ${p.cluId}` : ''} + ${(p.cropHistory ?? []).length > 0 ? `${p.cropHistory.length} yr history` : ''} + ${sourceLabel ? `${sourceLabel}` : ''} +
+
+
+ + + - - - - +
-
- -
- ${p.coordinates?.lat != null && p.coordinates?.lon != null ? `

📍 ${p.coordinates.lat.toFixed(4)}, ${p.coordinates.lon.toFixed(4)}

` : ''} - ${p.notes ? `

📝 ${p.notes}

` : ''} - ${p._source?.includes('ag-refine') ? `

↗ Synced from AG-Refine

` : ''} - ${agRefineUrl ? `
Open in AG-Refine ↗` : ''} -

Weather data: Phase 6

-

Carbon potential: Phase 7

-

Added ${new Date(p.createdAt).toLocaleDateString()}

+ +
+ ${p.coordinates?.lat != null && p.coordinates?.lon != null + ? `

📍 ${p.coordinates.lat.toFixed(4)}, ${p.coordinates.lon.toFixed(4)}

` + : ''} + ${p.notes ? `

📝 ${p.notes}

` : ''} + ${cropHistoryHtml} + ${harvestHtml} + ${!cropHistoryHtml && !harvestHtml + ? `

No crop history yet — ingest a harvest file to populate.

` + : ''} +
+

Added ${new Date(p.createdAt).toLocaleDateString()}

+
+ ${agRefineUrl ? `Open in AG-Refine ↗` : ''} + Carbon: Phase 7 +
+
+
-
- `).join(''); + `; + }).join(''); listEl.querySelectorAll('.agri-card').forEach((card) => { card.addEventListener('click', async (e) => { diff --git a/agrifine-extension/src/utils/agrefine-bridge.js b/agrifine-extension/src/utils/agrefine-bridge.js index 994aa8c08d..cbe065ecd9 100644 --- a/agrifine-extension/src/utils/agrefine-bridge.js +++ b/agrifine-extension/src/utils/agrefine-bridge.js @@ -135,6 +135,41 @@ function extractLoads(raw) { return loads; } +// Injected into AG-Refine tab to write field data back into its localStorage +function writeFieldsToAgRefineTab(fields) { + try { + localStorage.setItem('agrifine_pushed_fields', JSON.stringify(fields)); + localStorage.setItem('agrifine_pushed_at', new Date().toISOString()); + // Dispatch an event so a listening AG-Refine app can react immediately + window.dispatchEvent(new CustomEvent('agrifine:fields-updated', { detail: { fields } })); + return { ok: true, count: fields.length }; + } catch (err) { + return { ok: false, error: err.message }; + } +} + +export async function pushToAgRefine(profiles) { + const configuredUrl = await getAgRefineUrl(); + const allTabs = await chrome.tabs.query({}); + const agRefineTabs = allTabs.filter((t) => tabMatchesAgRefine(t, configuredUrl)); + + if (agRefineTabs.length === 0) { + return { ok: false, error: 'No AG-Refine tab found. Open AG-Refine first.' }; + } + + const tab = agRefineTabs[0]; + try { + const [result] = await chrome.scripting.executeScript({ + target: { tabId: tab.id }, + func: writeFieldsToAgRefineTab, + args: [profiles], + }); + return result.result; + } catch (err) { + return { ok: false, error: `Cannot write to AG-Refine tab: ${err.message}` }; + } +} + export async function syncFromAgRefine() { const configuredUrl = await getAgRefineUrl(); const allTabs = await chrome.tabs.query({}); diff --git a/agrifine-extension/tests/seed_and_screenshot.mjs b/agrifine-extension/tests/seed_and_screenshot.mjs new file mode 100644 index 0000000000..014608a9fd --- /dev/null +++ b/agrifine-extension/tests/seed_and_screenshot.mjs @@ -0,0 +1,319 @@ +/** + * Agrifine seeder + screenshot test + * Seeds all storage with realistic farm data and screenshots every tab. + * Run: PLAYWRIGHT_BROWSERS_PATH=/opt/pw-browsers node tests/seed_and_screenshot.mjs + */ + +import { chromium } from 'playwright'; +import { mkdirSync, existsSync } from 'fs'; +import { resolve, dirname } from 'path'; +import { fileURLToPath } from 'url'; + +const __dir = dirname(fileURLToPath(import.meta.url)); +const UNIT_ROOT = resolve(__dir, '..'); +const DIST = resolve(UNIT_ROOT, 'dist'); +const SCREENSHOTS = resolve(UNIT_ROOT, 'screenshots'); +const CHROMIUM = process.env.PLAYWRIGHT_BROWSERS_PATH + ? `${process.env.PLAYWRIGHT_BROWSERS_PATH}/chromium-1194/chrome-linux/chrome` + : '/opt/pw-browsers/chromium-1194/chrome-linux/chrome'; + +mkdirSync(SCREENSHOTS, { recursive: true }); + +// ── Seed data ──────────────────────────────────────────────────────────────── + +const SEED = { + agrifine_field_profiles: [ + { + id: 'fp_001', name: 'North 80', acres: 78.4, soilType: 'Silty Clay Loam', + cluId: 'IL-147-0412', coordinates: { lat: 41.8827, lon: -88.0071 }, + notes: 'Tile-drained. Historically strong corn yields. Slight compaction issue in SW corner.', + cropHistory: [ + { year: 2023, crop: 'Corn', yield: 212, unit: 'bu/ac' }, + { year: 2022, crop: 'Soybeans', yield: 58, unit: 'bu/ac' }, + { year: 2021, crop: 'Corn', yield: 198, unit: 'bu/ac' }, + { year: 2020, crop: 'Soybeans', yield: 54, unit: 'bu/ac' }, + ], + harvestRecords: [ + { date: '2023-10-14', crop: 'Corn', yield: 212, unit: 'bu/ac', moisture: 17.2 }, + { date: '2022-09-28', crop: 'Soybeans', yield: 58, unit: 'bu/ac', moisture: 13.1 }, + ], + carbonPotential: null, weatherData: null, + createdAt: '2024-01-15T10:00:00Z', _source: 'manual', + }, + { + id: 'fp_002', name: 'South Bottoms', acres: 112.0, soilType: 'Silt Loam', + cluId: 'IL-147-0413', coordinates: { lat: 41.8750, lon: -88.0120 }, + notes: 'Flood risk near creek. Reduced tillage program since 2021. Good organic matter.', + cropHistory: [ + { year: 2023, crop: 'Soybeans', yield: 62, unit: 'bu/ac' }, + { year: 2022, crop: 'Corn', yield: 205, unit: 'bu/ac' }, + { year: 2021, crop: 'Soybeans', yield: 57, unit: 'bu/ac' }, + ], + harvestRecords: [ + { date: '2023-09-22', crop: 'Soybeans', yield: 62, unit: 'bu/ac', moisture: 12.8 }, + ], + carbonPotential: null, weatherData: null, + createdAt: '2024-01-15T10:30:00Z', _source: 'manual', + }, + { + id: 'fp_003', name: 'East Pivot', acres: 134.5, soilType: 'Sandy Loam', + cluId: 'IL-147-0414', coordinates: { lat: 41.8900, lon: -87.9980 }, + notes: 'Center pivot irrigation. Drought-susceptible. Cover crop trial started 2022.', + cropHistory: [ + { year: 2023, crop: 'Corn', yield: 225, unit: 'bu/ac' }, + { year: 2022, crop: 'Corn', yield: 218, unit: 'bu/ac' }, + ], + harvestRecords: [ + { date: '2023-10-20', crop: 'Corn', yield: 225, unit: 'bu/ac', moisture: 15.9 }, + ], + carbonPotential: null, weatherData: null, + createdAt: '2024-01-16T08:00:00Z', _source: 'manual', + }, + ], + + agrifine_ingested_files: [ + { + id: 'file_001', filename: 'harvest_2023_fall.csv', type: 'CSV', + uploadedAt: '2024-01-10T14:22:00Z', + structuredData: { + operation_type: 'Corn Harvest', + fields: ['North 80', 'East Pivot'], + total_acres: 212.9, + avg_yield_bu_ac: 218.5, + avg_moisture_pct: 16.6, + equipment: 'Case IH 8250', + total_bushels: 46520, + }, + preview: 'operation_type: "Corn Harvest" | fields: ["North 80","East Pivot"] | total_acres: 212.9 | avg_yield_bu_ac: 218.5', + }, + { + id: 'file_002', filename: 'soil_test_results_2023.xlsx', type: 'Excel', + uploadedAt: '2024-01-08T09:15:00Z', + structuredData: { + test_date: '2023-08-15', + fields_tested: 3, + avg_ph: 6.8, + avg_p_ppm: 42, + avg_k_ppm: 180, + cec: 22.4, + organic_matter_pct: 3.8, + recommendations: 'Apply 200 lbs/ac 0-46-0 on South Bottoms before corn planting', + }, + preview: 'test_date: "2023-08-15" | avg_ph: 6.8 | avg_p_ppm: 42 | organic_matter_pct: 3.8', + }, + { + id: 'file_003', filename: 'input_expense_report_2023.pdf', type: 'PDF', + uploadedAt: '2024-01-05T16:40:00Z', + structuredData: { + year: 2023, + total_seed_cost: 48750, + total_fertilizer_cost: 112400, + total_chemical_cost: 31200, + total_fuel_cost: 22800, + total_expenses: 215150, + cost_per_acre: 652, + }, + preview: 'year: 2023 | total_seed_cost: 48750 | total_fertilizer_cost: 112400 | cost_per_acre: 652', + }, + ], + + agrifine_reading_list: [ + { + id: 'rl_001', + url: 'https://www.farmprogress.com/corn/usda-raises-corn-yield-forecast', + title: 'USDA Raises 2023 Corn Yield Forecast to 174.9 Bu/Acre', + savedAt: '2024-01-18T11:30:00Z', + summary: 'USDA updated its corn yield forecast to 174.9 bushels per acre for the 2023 crop, up 1.2 bu from the November estimate, citing favorable harvest conditions across the Corn Belt.', + tags: ['USDA', 'agriculture', 'finance'], + }, + { + id: 'rl_002', + url: 'https://www.dtnpf.com/agriculture/web/ag/news/article/2024/01/15/la-nina-threat-corn-belt-spring', + title: 'La Niña Threat Could Dry Out Corn Belt This Spring', + savedAt: '2024-01-17T09:15:00Z', + summary: 'Meteorologists warn a developing La Niña pattern may reduce spring rainfall across Illinois and Indiana, raising drought risk for early-planted corn. Irrigation demand could spike 20-30% above normal.', + tags: ['weather', 'agriculture'], + }, + { + id: 'rl_003', + url: 'https://www.agriculture.com/carbon-markets-eqip-2024', + title: 'EQIP Carbon Market Signup Opens February 2024', + savedAt: '2024-01-16T14:00:00Z', + summary: 'USDA NRCS opened signup for the Environmental Quality Incentives Program carbon track, offering $45-65 per acre for verified no-till and cover crop practices across enrolled fields.', + tags: ['carbon', 'USDA', 'finance'], + }, + ], + + agrifine_farm_memory: { + lastUpdated: '2024-01-18T12:00:00Z', + farm_name: 'Hendricks Family Farms', + total_acres: 324.9, + primary_crops: ['Corn', 'Soybeans'], + soil_overview: 'Mixed silty clay loam and sandy loam soils. Average OM 3.8%. Tile drainage on North 80. Flood risk on South Bottoms near creek.', + aiGeneratedSummary: 'Hendricks Family Farms operates 324.9 acres across three fields in Kane County, IL. The 2023 corn crop averaged 218.5 bu/ac — above county average — driven by strong yields on the irrigated East Pivot (225 bu/ac). South Bottoms carries meaningful flood risk but benefits from 3+ years of reduced tillage. Soil tests indicate adequate fertility; phosphorus application recommended on South Bottoms ahead of 2024 corn rotation. Total 2023 input costs of $215,150 ($652/ac) leave healthy margin at current corn prices.', + key_insights: [ + 'East Pivot leads yield performance at 225 bu/ac with irrigation advantage', + 'South Bottoms organic matter at 3.8% — reduced tillage program paying off', + 'Total operation averaged 218.5 bu/ac corn in 2023 vs 174.9 national average', + 'EQIP carbon program could generate $14,620–$21,118 annually across enrolled acres', + ], + action_items: [ + 'Apply 200 lbs/ac 0-46-0 on South Bottoms before corn planting', + 'Evaluate EQIP carbon signup before February deadline', + 'Address SW corner compaction on North 80 — consider subsoiling pass', + 'Extend cover crop trial from East Pivot to North 80', + ], + risk_flags: [ + 'La Niña pattern threatens spring drought — irrigation scheduling critical for East Pivot', + 'South Bottoms flood risk near creek — monitor spring soil moisture before planting', + 'Input costs at $652/ac — watch corn futures for margin compression', + ], + opportunities: [ + 'Carbon credit program: $45-65/ac for documented no-till + cover crops', + 'East Pivot irrigation gives drought hedge — consider expanding irrigated acres', + 'Soybean rotation on North 80 in 2024 — lower input cost year', + ], + }, +}; + +async function main() { + if (!existsSync(DIST + '/manifest.json')) { + console.error('ERROR: dist/ not found. Run: npm run build'); + process.exit(1); + } + + console.log('Launching Chrome with seeded Agrifine extension…'); + + const context = await chromium.launchPersistentContext('', { + executablePath: CHROMIUM, + headless: true, + args: [ + `--disable-extensions-except=${DIST}`, + `--load-extension=${DIST}`, + '--no-sandbox', + '--disable-setuid-sandbox', + '--disable-dev-shm-usage', + '--disable-gpu', + ], + }); + + const page = await context.newPage(); + + // Stub chrome.* with SEEDED data + await page.addInitScript((seed) => { + const store = { ...seed }; + const sessionStore = {}; + + window.chrome = { + storage: { + local: { + get: (k, cb) => setTimeout(() => cb({ [k]: store[k] ?? null }), 0), + set: (obj, cb) => { Object.assign(store, obj); cb && cb(); }, + }, + session: { + get: (k, cb) => setTimeout(() => cb({ [k]: sessionStore[k] ?? null }), 0), + set: (obj, cb) => { Object.assign(sessionStore, obj); cb && cb(); }, + }, + }, + runtime: { + sendMessage: (_msg, cb) => cb && setTimeout(() => cb({ error: 'No background in test mode' }), 0), + connect: () => ({ onDisconnect: { addListener: () => {} } }), + lastError: null, + }, + tabs: { + query: (_q, cb) => cb([{ id: 1, url: 'https://farmprogress.com/corn', title: 'Farm Progress' }]), + sendMessage: (_id, _msg, cb) => cb && cb({ text: 'test page content' }), + }, + sidePanel: { setPanelBehavior: () => Promise.resolve() }, + scripting: { executeScript: (_opts, cb) => cb && cb([{ result: {} }]) }, + }; + }, SEED); + + await page.goto(`file://${DIST}/sidebar.html`); + await page.waitForSelector('#main-content', { timeout: 8000 }); + await page.waitForTimeout(600); + + async function ss(name) { + const file = `${SCREENSHOTS}/${name}.png`; + await page.screenshot({ path: file }); + console.log(` Screenshot: ${file}`); + return file; + } + + async function tab(name) { + const TAB_MAP = { + reading: '[data-tab="reading-list"]', + ingest: '[data-tab="data-ingest"]', + fields: '[data-tab="field-profile"]', + dashboard: '[data-tab="dashboard"]', + carbon: '[data-tab="carbon-estimator"]', + agent: '[data-tab="ag-refine"]', + }; + await page.click(TAB_MAP[name]); + await page.waitForTimeout(500); + } + + // ── 1. Reading List (default tab) ────────────────────────────────────────── + console.log('\n[1/6] Reading List tab'); + await ss('01_reading_list'); + + // ── 2. Data Ingest ───────────────────────────────────────────────────────── + console.log('\n[2/6] Data Ingest tab'); + await tab('ingest'); + await ss('02_data_ingest'); + + // ── 3. Field Profiles ────────────────────────────────────────────────────── + console.log('\n[3/6] Field Profiles tab'); + await tab('fields'); + await page.waitForTimeout(300); + await ss('03_field_profiles'); + + // Expand first field + const firstCard = page.locator('.agri-card').first(); + if (await firstCard.count()) { + await firstCard.click(); + await page.waitForTimeout(400); + await ss('03b_field_expanded'); + } + + // ── 4. Dashboard ────────────────────────────────────────────────────────── + console.log('\n[4/6] Dashboard tab'); + await tab('dashboard'); + await page.waitForTimeout(400); + await ss('04_dashboard'); + + // ── 5. Carbon Estimator ─────────────────────────────────────────────────── + console.log('\n[5/6] Carbon Estimator tab'); + await tab('carbon'); + await ss('05_carbon'); + + // ── 6. AgriAgent ───────────────────────────────────────────────────────── + console.log('\n[6/6] AgriAgent tab'); + await tab('agent'); + await ss('06_agent'); + + // ── Probe: settings panel ───────────────────────────────────────────────── + console.log('\n[probe] Settings panel'); + await page.click('#btn-settings'); + await page.waitForTimeout(300); + await ss('07_settings_panel'); + + // ── Probe: new field form ───────────────────────────────────────────────── + console.log('\n[probe] New field form'); + await tab('fields'); + await page.waitForTimeout(300); + await page.click('#fp-new-btn'); + await page.waitForTimeout(300); + await ss('08_new_field_form'); + + // ── Report what is and isn't visible ───────────────────────────────────── + const fieldCount = await page.evaluate(() => + document.querySelectorAll('.agri-card').length + ); + console.log(`\nField cards visible after form open: ${fieldCount}`); + + await context.close(); + console.log('\nAll screenshots saved to:', SCREENSHOTS); +} + +main().catch(err => { console.error(err); process.exit(1); }); From f70ace8514ce10ba6a7a1eed76ec401d1c4677e4 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 27 Jun 2026 20:11:41 +0000 Subject: [PATCH 14/17] Add Boardroom multi-agent committee to AgriAgent tab MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a dual-mode AGENT / BOARDROOM toggle to the AgriAgent tab: AGENT mode (unchanged): single AgriAgent chat with full tool loop, farm memory, and agentic reasoning. BOARDROOM mode: four named committee members hold a structured meeting over the farm's live data context. Committee roster: 💹 Kount Kuekkens — CFO / Financials (spiraling economist, Dairy Moneyball math) 🌾 Rolf Forage — Agronomist / Crops (opinionated, field-first) 🐄 Dr. Vera Hest — Chief Veterinarian / Herd Health (precise, biological thresholds) 📋 Marla Shift — Operations Manager / Personnel (reality-check, labor constraints) Each agent makes an independent streaming API call with a persona-specific system prompt. Agents run sequentially so later speakers receive a full transcript of what earlier colleagues said, enabling authentic cross-domain commentary (e.g. Vera referencing Rolf's harvest quality point). After the initial round, a target bar appears for cross-examination: the chair can direct follow-ups to All, or to a specific advisor by name. Five boardroom-specific suggested topics replace the standard agent prompts in this mode (weekly audit, data integrity, heat stress, risk per dept, biggest cross-dept contention). Co-Authored-By: Claude Sonnet 4.6 Claude-Session: https://claude.ai/code/session_01KBD2dN2KEjzz3UQFa9hEpu --- agrifine-extension/dist/sidebar.js | 578 ++++++++++++++++-- agrifine-extension/screenshots/agent_mode.png | Bin 0 -> 50877 bytes .../screenshots/boardroom_mode.png | Bin 0 -> 57574 bytes agrifine-extension/src/ag-refine/committee.js | 153 +++++ agrifine-extension/src/ag-refine/index.js | 493 +++++++++++---- 5 files changed, 1050 insertions(+), 174 deletions(-) create mode 100644 agrifine-extension/screenshots/agent_mode.png create mode 100644 agrifine-extension/screenshots/boardroom_mode.png create mode 100644 agrifine-extension/src/ag-refine/committee.js diff --git a/agrifine-extension/dist/sidebar.js b/agrifine-extension/dist/sidebar.js index 9937731b35..c17c74db82 100644 --- a/agrifine-extension/dist/sidebar.js +++ b/agrifine-extension/dist/sidebar.js @@ -293,6 +293,215 @@ var AgrifineAgent = /*#__PURE__*/function () { /***/ }, +/***/ "./src/ag-refine/committee.js" +/*!************************************!*\ + !*** ./src/ag-refine/committee.js ***! + \************************************/ +(__unused_webpack_module, __webpack_exports__, __webpack_require__) { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ COMMITTEE: () => (/* binding */ COMMITTEE), +/* harmony export */ runCommitteeAgent: () => (/* binding */ runCommitteeAgent) +/* harmony export */ }); +/* harmony import */ var _utils_storage_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../utils/storage.js */ "./src/utils/storage.js"); +function _regenerator() { /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/babel/babel/blob/main/packages/babel-helpers/LICENSE */ var e, t, r = "function" == typeof Symbol ? Symbol : {}, n = r.iterator || "@@iterator", o = r.toStringTag || "@@toStringTag"; function i(r, n, o, i) { var c = n && n.prototype instanceof Generator ? n : Generator, u = Object.create(c.prototype); return _regeneratorDefine2(u, "_invoke", function (r, n, o) { var i, c, u, f = 0, p = o || [], y = !1, G = { p: 0, n: 0, v: e, a: d, f: d.bind(e, 4), d: function d(t, r) { return i = t, c = 0, u = e, G.n = r, a; } }; function d(r, n) { for (c = r, u = n, t = 0; !y && f && !o && t < p.length; t++) { var o, i = p[t], d = G.p, l = i[2]; r > 3 ? (o = l === n) && (u = i[(c = i[4]) ? 5 : (c = 3, 3)], i[4] = i[5] = e) : i[0] <= d && ((o = r < 2 && d < i[1]) ? (c = 0, G.v = n, G.n = i[1]) : d < l && (o = r < 3 || i[0] > n || n > l) && (i[4] = r, i[5] = n, G.n = l, c = 0)); } if (o || r > 1) return a; throw y = !0, n; } return function (o, p, l) { if (f > 1) throw TypeError("Generator is already running"); for (y && 1 === p && d(p, l), c = p, u = l; (t = c < 2 ? e : u) || !y;) { i || (c ? c < 3 ? (c > 1 && (G.n = -1), d(c, u)) : G.n = u : G.v = u); try { if (f = 2, i) { if (c || (o = "next"), t = i[o]) { if (!(t = t.call(i, u))) throw TypeError("iterator result is not an object"); if (!t.done) return t; u = t.value, c < 2 && (c = 0); } else 1 === c && (t = i["return"]) && t.call(i), c < 2 && (u = TypeError("The iterator does not provide a '" + o + "' method"), c = 1); i = e; } else if ((t = (y = G.n < 0) ? u : r.call(n, G)) !== a) break; } catch (t) { i = e, c = 1, u = t; } finally { f = 1; } } return { value: t, done: y }; }; }(r, o, i), !0), u; } var a = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} t = Object.getPrototypeOf; var c = [][n] ? t(t([][n]())) : (_regeneratorDefine2(t = {}, n, function () { return this; }), t), u = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(c); function f(e) { return Object.setPrototypeOf ? Object.setPrototypeOf(e, GeneratorFunctionPrototype) : (e.__proto__ = GeneratorFunctionPrototype, _regeneratorDefine2(e, o, "GeneratorFunction")), e.prototype = Object.create(u), e; } return GeneratorFunction.prototype = GeneratorFunctionPrototype, _regeneratorDefine2(u, "constructor", GeneratorFunctionPrototype), _regeneratorDefine2(GeneratorFunctionPrototype, "constructor", GeneratorFunction), GeneratorFunction.displayName = "GeneratorFunction", _regeneratorDefine2(GeneratorFunctionPrototype, o, "GeneratorFunction"), _regeneratorDefine2(u), _regeneratorDefine2(u, o, "Generator"), _regeneratorDefine2(u, n, function () { return this; }), _regeneratorDefine2(u, "toString", function () { return "[object Generator]"; }), (_regenerator = function _regenerator() { return { w: i, m: f }; })(); } +function _regeneratorDefine2(e, r, n, t) { var i = Object.defineProperty; try { i({}, "", {}); } catch (e) { i = 0; } _regeneratorDefine2 = function _regeneratorDefine(e, r, n, t) { function o(r, n) { _regeneratorDefine2(e, r, function (e) { return this._invoke(r, n, e); }); } r ? i ? i(e, r, { value: n, enumerable: !t, configurable: !t, writable: !t }) : e[r] = n : (o("next", 0), o("throw", 1), o("return", 2)); }, _regeneratorDefine2(e, r, n, t); } +function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t["return"] || t["return"](); } finally { if (u) throw o; } } }; } +function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } } +function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; } +function asyncGeneratorStep(n, t, e, r, o, a, c) { try { var i = n[a](c), u = i.value; } catch (n) { return void e(n); } i.done ? t(u) : Promise.resolve(u).then(r, o); } +function _asyncToGenerator(n) { return function () { var t = this, e = arguments; return new Promise(function (r, o) { var a = n.apply(t, e); function _next(n) { asyncGeneratorStep(a, r, o, _next, _throw, "next", n); } function _throw(n) { asyncGeneratorStep(a, r, o, _next, _throw, "throw", n); } _next(void 0); }); }; } +/** + * The Boardroom — Multi-Agent Audit Committee + * + * Four named advisors, each with a domain-specific persona, review the same + * farm data context and report in sequence. Later agents receive a summary of + * what earlier agents said, enabling authentic cross-domain commentary. + */ + +var MODEL = 'claude-sonnet-4-6'; +var ANTHROPIC_URL = 'https://api.anthropic.com/v1/messages'; +var COMMITTEE = [{ + id: 'financials', + name: 'Kount Kuekkens', + role: 'CFO · Financials', + emoji: '💹', + accentColor: '#d97706', + borderStyle: 'border-l-[3px]', + borderColor: '#d97706', + persona: "You are Kount Kuekkens, a retired agricultural economist now serving as CFO for this farm operation. You speak with a spiraling rhetorical style \u2014 you begin broad, drift into economic theory or historical context, but always land on concrete \"Dairy Moneyball\" math that actually matters.\n\nYour domain: Income Over Feed Cost (IOFC), commodity price impacts on margins, feed efficiency ratios, processor quality premium/penalty thresholds, cash flow position, budget variances, and the financial consequences of operational data errors.\n\nWhen you spot data problems, quantify the financial blindspot they create. When you see opportunities, express them in dollar terms. You are candid about when you are \"spiraling\" into uncertainty vs. when you have hard numbers. You occasionally reference obscure economic principles before getting to the point.\n\nReport in 3\u20134 paragraphs. Be specific \u2014 name dollar figures, percentages, and cite the data points you are drawing from." +}, { + id: 'crops', + name: 'Rolf Forage', + role: 'Agronomist · Crops', + emoji: '🌾', + accentColor: '#16a34a', + borderColor: '#16a34a', + persona: "You are Rolf Forage (pronounced \"For-ahh-juz\"), a fiercely opinionated agronomist and crops director. You do not care about spreadsheets or financial models \u2014 you care about what is actually in the field and the bunker right now.\n\nYour domain: forage quality (dry matter, NDF, fiber digestibility), silage inventory and fermentation integrity, harvest timing windows, field conditions (soil type, drainage, compaction), cover crop programs, nutrient cycling, and input scheduling.\n\nYou are demanding and direct. If the data shows a crop problem that will compromise feed quality, you say so loudly and insist it be corrected immediately \u2014 you do not sugarcoat risk to protect someone's budget. You will call out the financial team for cutting corners that ultimately cost more in lost production. You speak in practical, field-level language.\n\nReport in 3\u20134 paragraphs. Be opinionated and specific about what needs to happen and when." +}, { + id: 'herd', + name: 'Dr. Vera Hest', + role: 'Chief Veterinarian · Herd Health', + emoji: '🐄', + accentColor: '#60a5fa', + borderColor: '#60a5fa', + persona: "You are Dr. Vera Hest, a sharp-witted, data-driven veterinarian and herd health director. You value biological metrics and animal welfare above all else \u2014 and you will challenge any department that proposes to compromise herd health in the name of cost savings or operational convenience.\n\nYour domain: Somatic Cell Count (SCC) trends and penalty risk, Dry Matter Intake (DMI) per cow, Body Condition Score (BCS), transition cow health, Temperature-Humidity Index (THI) and heat stress protocol, milk component trends (fat, protein), reproductive performance, and disease incidence (ketosis, mastitis, lameness, displaced abomasum).\n\nYou connect biological metrics to production outcomes \u2014 a BCS over 3.75 at calving means dystocia and ketosis next month; a THI of 86 means DMI drops 10\u201315% and milk yield follows within 48 hours. You are precise with thresholds, not vague. You speak clinically but translate findings for the group when needed.\n\nReport in 3\u20134 paragraphs. Be incisive. Cite specific thresholds and explain their downstream consequences." +}, { + id: 'personnel', + name: 'Marla Shift', + role: 'Operations Manager · Personnel', + emoji: '📋', + accentColor: '#94a3b8', + borderColor: '#94a3b8', + persona: "You are Marla Shift, the operations-hardened manager who oversees labor, personnel scheduling, equipment maintenance, and day-to-day execution. You are the \"reality check\" of the boardroom.\n\nYour domain: labor availability and shift coverage, overtime costs and crew fatigue, equipment uptime and maintenance backlogs, safety compliance, training gaps, and operational root causes of data errors or production misses.\n\nWhen the other advisors make demands \u2014 Rolf needs an early harvest crew, Vera wants manual pen checks every two hours, Kount wants a new validation system built by Friday \u2014 you translate those demands into actual execution requirements: how many people, how many hours, what it costs, and what else will be delayed or skipped to make it happen.\n\nYou provide honest operational explanations (not excuses) for why things went wrong: mechanical failures, staffing gaps during peak periods, training slips under pressure. You are pragmatic, occasionally exasperated, and very good at finding workarounds under real-world constraints.\n\nReport in 3\u20134 paragraphs. Be concrete about labor, time, and resource constraints." +}]; + +/** + * Run a single committee agent and stream their response. + * priorStatements: array of { name, role, text } from agents who already spoke. + * onChunk: called with partial text as it streams in. + */ +function runCommitteeAgent(_x, _x2, _x3, _x4) { + return _runCommitteeAgent.apply(this, arguments); +} +function _runCommitteeAgent() { + _runCommitteeAgent = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee(agent, topic, priorStatements, onChunk) { + var apiKey, contextBundle, priorContext, systemPrompt, res, text, reader, decoder, fullText, buffer, _lines$pop, _yield$reader$read, done, value, lines, _iterator, _step, line, payload, _evt$delta, evt, _t; + return _regenerator().w(function (_context) { + while (1) switch (_context.p = _context.n) { + case 0: + _context.n = 1; + return (0,_utils_storage_js__WEBPACK_IMPORTED_MODULE_0__.sessionGet)(_utils_storage_js__WEBPACK_IMPORTED_MODULE_0__.KEYS.API_KEY); + case 1: + apiKey = _context.v; + if (apiKey) { + _context.n = 2; + break; + } + throw new Error('No API key set — open ⚙ Settings to add your Anthropic key.'); + case 2: + _context.n = 3; + return (0,_utils_storage_js__WEBPACK_IMPORTED_MODULE_0__.buildContextBundle)(); + case 3: + contextBundle = _context.v; + priorContext = priorStatements.length > 0 ? "\n\n\u2500\u2500 PRIOR STATEMENTS FROM YOUR COLLEAGUES \u2500\u2500\n".concat(priorStatements.map(function (s) { + return "".concat(s.name, " (").concat(s.role, "):\n").concat(s.text); + }).join('\n\n─────────────────────\n\n')) : ''; + systemPrompt = "".concat(agent.persona, "\n\n\u2500\u2500 FARM DATA CONTEXT \u2500\u2500\n").concat(contextBundle).concat(priorContext, "\n\nYou are presenting your department report at the weekly audit boardroom. Address the meeting topic directly from your domain's perspective. If colleagues have already spoken, you may reference or push back on their points where they intersect with your domain."); + _context.n = 4; + return fetch(ANTHROPIC_URL, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'x-api-key': apiKey, + 'anthropic-version': '2023-06-01', + 'anthropic-dangerous-direct-browser-access': 'true' + }, + body: JSON.stringify({ + model: MODEL, + max_tokens: 1024, + stream: true, + system: systemPrompt, + messages: [{ + role: 'user', + content: topic + }] + }) + }); + case 4: + res = _context.v; + if (res.ok) { + _context.n = 6; + break; + } + _context.n = 5; + return res.text(); + case 5: + text = _context.v; + throw new Error("API ".concat(res.status, ": ").concat(text)); + case 6: + // Stream SSE response + reader = res.body.getReader(); + decoder = new TextDecoder(); + fullText = ''; + buffer = ''; + case 7: + if (false) // removed by dead control flow +{} + _context.n = 8; + return reader.read(); + case 8: + _yield$reader$read = _context.v; + done = _yield$reader$read.done; + value = _yield$reader$read.value; + if (!done) { + _context.n = 9; + break; + } + return _context.a(3, 19); + case 9: + buffer += decoder.decode(value, { + stream: true + }); + lines = buffer.split('\n'); + buffer = (_lines$pop = lines.pop()) !== null && _lines$pop !== void 0 ? _lines$pop : ''; + _iterator = _createForOfIteratorHelper(lines); + _context.p = 10; + _iterator.s(); + case 11: + if ((_step = _iterator.n()).done) { + _context.n = 15; + break; + } + line = _step.value; + if (line.startsWith('data: ')) { + _context.n = 12; + break; + } + return _context.a(3, 14); + case 12: + payload = line.slice(6).trim(); + if (!(payload === '[DONE]')) { + _context.n = 13; + break; + } + return _context.a(3, 14); + case 13: + try { + evt = JSON.parse(payload); + if (evt.type === 'content_block_delta' && ((_evt$delta = evt.delta) === null || _evt$delta === void 0 ? void 0 : _evt$delta.type) === 'text_delta') { + fullText += evt.delta.text; + onChunk(evt.delta.text, fullText); + } + } catch (_) {} + case 14: + _context.n = 11; + break; + case 15: + _context.n = 17; + break; + case 16: + _context.p = 16; + _t = _context.v; + _iterator.e(_t); + case 17: + _context.p = 17; + _iterator.f(); + return _context.f(17); + case 18: + _context.n = 7; + break; + case 19: + return _context.a(2, fullText); + } + }, _callee, null, [[10, 16, 17, 18]]); + })); + return _runCommitteeAgent.apply(this, arguments); +} + +/***/ }, + /***/ "./src/ag-refine/index.js" /*!********************************!*\ !*** ./src/ag-refine/index.js ***! @@ -304,11 +513,18 @@ __webpack_require__.r(__webpack_exports__); /* harmony export */ AgRefineModule: () => (/* binding */ AgRefineModule) /* harmony export */ }); /* harmony import */ var _agent_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./agent.js */ "./src/ag-refine/agent.js"); +/* harmony import */ var _committee_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./committee.js */ "./src/ag-refine/committee.js"); +function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); } +function _regeneratorValues(e) { if (null != e) { var t = e["function" == typeof Symbol && Symbol.iterator || "@@iterator"], r = 0; if (t) return t.call(e); if ("function" == typeof e.next) return e; if (!isNaN(e.length)) return { next: function next() { return e && r >= e.length && (e = void 0), { value: e && e[r++], done: !e }; } }; } throw new TypeError(_typeof(e) + " is not iterable"); } +function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t["return"] || t["return"](); } finally { if (u) throw o; } } }; } +function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } } +function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; } function _regenerator() { /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/babel/babel/blob/main/packages/babel-helpers/LICENSE */ var e, t, r = "function" == typeof Symbol ? Symbol : {}, n = r.iterator || "@@iterator", o = r.toStringTag || "@@toStringTag"; function i(r, n, o, i) { var c = n && n.prototype instanceof Generator ? n : Generator, u = Object.create(c.prototype); return _regeneratorDefine2(u, "_invoke", function (r, n, o) { var i, c, u, f = 0, p = o || [], y = !1, G = { p: 0, n: 0, v: e, a: d, f: d.bind(e, 4), d: function d(t, r) { return i = t, c = 0, u = e, G.n = r, a; } }; function d(r, n) { for (c = r, u = n, t = 0; !y && f && !o && t < p.length; t++) { var o, i = p[t], d = G.p, l = i[2]; r > 3 ? (o = l === n) && (u = i[(c = i[4]) ? 5 : (c = 3, 3)], i[4] = i[5] = e) : i[0] <= d && ((o = r < 2 && d < i[1]) ? (c = 0, G.v = n, G.n = i[1]) : d < l && (o = r < 3 || i[0] > n || n > l) && (i[4] = r, i[5] = n, G.n = l, c = 0)); } if (o || r > 1) return a; throw y = !0, n; } return function (o, p, l) { if (f > 1) throw TypeError("Generator is already running"); for (y && 1 === p && d(p, l), c = p, u = l; (t = c < 2 ? e : u) || !y;) { i || (c ? c < 3 ? (c > 1 && (G.n = -1), d(c, u)) : G.n = u : G.v = u); try { if (f = 2, i) { if (c || (o = "next"), t = i[o]) { if (!(t = t.call(i, u))) throw TypeError("iterator result is not an object"); if (!t.done) return t; u = t.value, c < 2 && (c = 0); } else 1 === c && (t = i["return"]) && t.call(i), c < 2 && (u = TypeError("The iterator does not provide a '" + o + "' method"), c = 1); i = e; } else if ((t = (y = G.n < 0) ? u : r.call(n, G)) !== a) break; } catch (t) { i = e, c = 1, u = t; } finally { f = 1; } } return { value: t, done: y }; }; }(r, o, i), !0), u; } var a = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} t = Object.getPrototypeOf; var c = [][n] ? t(t([][n]())) : (_regeneratorDefine2(t = {}, n, function () { return this; }), t), u = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(c); function f(e) { return Object.setPrototypeOf ? Object.setPrototypeOf(e, GeneratorFunctionPrototype) : (e.__proto__ = GeneratorFunctionPrototype, _regeneratorDefine2(e, o, "GeneratorFunction")), e.prototype = Object.create(u), e; } return GeneratorFunction.prototype = GeneratorFunctionPrototype, _regeneratorDefine2(u, "constructor", GeneratorFunctionPrototype), _regeneratorDefine2(GeneratorFunctionPrototype, "constructor", GeneratorFunction), GeneratorFunction.displayName = "GeneratorFunction", _regeneratorDefine2(GeneratorFunctionPrototype, o, "GeneratorFunction"), _regeneratorDefine2(u), _regeneratorDefine2(u, o, "Generator"), _regeneratorDefine2(u, n, function () { return this; }), _regeneratorDefine2(u, "toString", function () { return "[object Generator]"; }), (_regenerator = function _regenerator() { return { w: i, m: f }; })(); } function _regeneratorDefine2(e, r, n, t) { var i = Object.defineProperty; try { i({}, "", {}); } catch (e) { i = 0; } _regeneratorDefine2 = function _regeneratorDefine(e, r, n, t) { function o(r, n) { _regeneratorDefine2(e, r, function (e) { return this._invoke(r, n, e); }); } r ? i ? i(e, r, { value: n, enumerable: !t, configurable: !t, writable: !t }) : e[r] = n : (o("next", 0), o("throw", 1), o("return", 2)); }, _regeneratorDefine2(e, r, n, t); } function asyncGeneratorStep(n, t, e, r, o, a, c) { try { var i = n[a](c), u = i.value; } catch (n) { return void e(n); } i.done ? t(u) : Promise.resolve(u).then(r, o); } function _asyncToGenerator(n) { return function () { var t = this, e = arguments; return new Promise(function (r, o) { var a = n.apply(t, e); function _next(n) { asyncGeneratorStep(a, r, o, _next, _throw, "next", n); } function _throw(n) { asyncGeneratorStep(a, r, o, _next, _throw, "throw", n); } _next(void 0); }); }; } + var TOOL_ICONS = { get_reading_list: '📖', get_field_profiles: '🌱', @@ -324,10 +540,20 @@ var TOOL_ICONS = { get_farm_memory: '🧠', update_farm_memory: '💾' }; -var SUGGESTED_PROMPTS = ['Review all my farm data and build a farm memory summary', 'What are my current field conditions and harvest windows?', 'What risks or opportunities do you see across my operation?', 'Screenshot this page and tell me what agricultural data you see', 'Export my reading list and field profiles to CSV']; +var AGENT_PROMPTS = ['Review all my farm data and build a farm memory summary', 'What are my current field conditions and harvest windows?', 'What risks or opportunities do you see across my operation?', 'Screenshot this page and tell me what agricultural data you see', 'Export my reading list and field profiles to CSV']; +var BOARDROOM_PROMPTS = ['Weekly operations audit — all departments, give me your status', 'We have a data integrity issue — assess the impact by department', 'Heat stress event incoming — what does each department need?', 'Review all farm data and identify the single biggest risk per department', 'What is the biggest point of contention between departments right now?']; function AgRefineModule() { - var messages = []; - var isRunning = false; + var mode = 'agent'; // 'agent' | 'boardroom' + + // Agent mode state + var agentMessages = []; + var agentRunning = false; + + // Boardroom mode state + var boardMessages = []; // { type: 'topic'|'report'|'chair'|'thinking', ... } + var boardRunning = false; + var boardTargetAgent = null; // null = all, or agent id for targeted response + return { id: 'ag-refine', label: 'AgriAgent', @@ -337,46 +563,116 @@ function AgRefineModule() { return _regenerator().w(function (_context) { while (1) switch (_context.n) { case 0: - container.innerHTML = "\n
\n\n \n
\n
\n \uD83E\uDD16\n

AgriAgent

\n AI Agent\n
\n

Multi-step reasoning over all your farm data

\n
\n\n \n
\n\n \n
\n

Try asking\u2026

\n
\n ".concat(SUGGESTED_PROMPTS.map(function (p) { - return "\n "); - }).join(''), "\n
\n
\n\n \n
\n
\n \n \n
\n \n
\n
\n "); + container.innerHTML = _this._html(); _this._bindEvents(container); - _this._renderMessages(container); + _this._switchMode(mode, container); case 1: return _context.a(2); } }, _callee); }))(); }, + _html: function _html() { + return "\n
\n\n \n
\n \n \n
\n\n \n
\n\n
\n
\n \uD83E\uDD16\n

AgriAgent

\n AI Agent\n
\n

Multi-step reasoning over all your farm data

\n
\n\n
\n\n
\n

Try asking\u2026

\n
\n ".concat(AGENT_PROMPTS.map(function (p) { + return "\n "); + }).join(''), "\n
\n
\n\n
\n
\n \n \n
\n \n
\n
\n\n \n
\n\n \n
\n
\n \uD83C\uDFDB\uFE0F\n

The Boardroom

\n
\n
\n ").concat(_committee_js__WEBPACK_IMPORTED_MODULE_1__.COMMITTEE.map(function (a) { + return "\n
\n ").concat(a.emoji, "\n ").concat(a.name, "\n
\n "); + }).join(''), "\n
\n
\n\n \n
\n\n \n
\n

Call the meeting to order\u2026

\n
\n ").concat(BOARDROOM_PROMPTS.map(function (p) { + return "\n "); + }).join(''), "\n
\n
\n\n \n
\n \n
\n Address:\n \n ").concat(_committee_js__WEBPACK_IMPORTED_MODULE_1__.COMMITTEE.map(function (a) { + return "\n \n "); + }).join(''), "\n
\n\n
\n \n \n
\n \n
\n
\n\n
\n "); + }, + _switchMode: function _switchMode(newMode, container) { + mode = newMode; + container.querySelector('#panel-agent').classList.toggle('hidden', mode !== 'agent'); + container.querySelector('#panel-boardroom').classList.toggle('hidden', mode !== 'boardroom'); + container.querySelectorAll('.mode-btn').forEach(function (btn) { + var active = btn.dataset.mode === mode; + btn.style.color = active ? '#22c55e' : '#3d4f66'; + btn.style.borderBottom = active ? '2px solid #22c55e' : '2px solid transparent'; + }); + }, _bindEvents: function _bindEvents(container) { var _this2 = this; - var input = container.querySelector('#agent-input'); - var sendBtn = container.querySelector('#agent-send'); - var send = function send() { - var text = input.value.trim(); - if (!text || isRunning) return; - input.value = ''; + // Mode toggle + container.querySelectorAll('.mode-btn').forEach(function (btn) { + btn.addEventListener('click', function () { + return _this2._switchMode(btn.dataset.mode, container); + }); + }); + + // ── Agent mode ── + var agentInput = container.querySelector('#agent-input'); + var agentSend = container.querySelector('#agent-send'); + var sendAgent = function sendAgent() { + var text = agentInput.value.trim(); + if (!text || agentRunning) return; + agentInput.value = ''; _this2._runAgent(text, container); }; - sendBtn.addEventListener('click', send); - input.addEventListener('keydown', function (e) { + agentSend.addEventListener('click', sendAgent); + agentInput.addEventListener('keydown', function (e) { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); - send(); + sendAgent(); } }); - container.querySelectorAll('.suggest-btn').forEach(function (btn) { + container.querySelectorAll('.agent-suggest-btn').forEach(function (btn) { btn.addEventListener('click', function () { - input.value = btn.textContent.trim(); - send(); + agentInput.value = btn.textContent.trim(); + sendAgent(); }); }); container.querySelector('#agent-clear').addEventListener('click', function () { - messages = []; - isRunning = false; - _this2._renderMessages(container); + agentMessages = []; + agentRunning = false; + _this2._renderAgentMessages(container); + }); + + // ── Boardroom mode ── + var boardInput = container.querySelector('#board-input'); + var boardSend = container.querySelector('#board-send'); + var sendBoard = function sendBoard() { + var text = boardInput.value.trim(); + if (!text || boardRunning) return; + boardInput.value = ''; + _this2._runBoardroom(text, container); + }; + boardSend.addEventListener('click', sendBoard); + boardInput.addEventListener('keydown', function (e) { + if (e.key === 'Enter' && !e.shiftKey) { + e.preventDefault(); + sendBoard(); + } + }); + container.querySelectorAll('.board-suggest-btn').forEach(function (btn) { + btn.addEventListener('click', function () { + boardInput.value = btn.textContent.trim(); + sendBoard(); + }); + }); + container.querySelector('#board-clear').addEventListener('click', function () { + boardMessages = []; + boardRunning = false; + boardTargetAgent = null; + container.querySelector('#board-target-bar').classList.add('hidden'); + _this2._renderBoardMessages(container); + }); + + // Target agent selector + container.querySelectorAll('.target-btn').forEach(function (btn) { + btn.addEventListener('click', function () { + boardTargetAgent = btn.dataset.target === 'all' ? null : btn.dataset.target; + container.querySelectorAll('.target-btn').forEach(function (b) { + b.classList.toggle('active-target', b.dataset.target === (boardTargetAgent !== null && boardTargetAgent !== void 0 ? boardTargetAgent : 'all')); + b.style.borderColor = b.classList.contains('active-target') ? '#22c55e' : ''; + if (!b.classList.contains('active-target')) b.style.borderColor = '#253047'; + }); + }); }); }, + // ── Agent mode logic ────────────────────────────────────────────────────── _runAgent: function _runAgent(userText, container) { var _this3 = this; return _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee2() { @@ -385,33 +681,27 @@ function AgRefineModule() { return _regenerator().w(function (_context2) { while (1) switch (_context2.p = _context2.n) { case 0: - if (!isRunning) { + if (!agentRunning) { _context2.n = 1; break; } return _context2.a(2); case 1: - isRunning = true; - - // Hide suggestions + agentRunning = true; (_container$querySelec = container.querySelector('#agent-suggestions')) === null || _container$querySelec === void 0 || _container$querySelec.classList.add('hidden'); - - // Add user message - messages.push({ + agentMessages.push({ role: 'user', text: userText }); - _this3._renderMessages(container); - - // Thinking placeholder + _this3._renderAgentMessages(container); thinkingId = "thinking_".concat(Date.now()); - messages.push({ + agentMessages.push({ role: 'thinking', id: thinkingId, steps: [] }); - _this3._renderMessages(container); - thinkingMsg = messages[messages.length - 1]; + _this3._renderAgentMessages(container); + thinkingMsg = agentMessages[agentMessages.length - 1]; agent = new _agent_js__WEBPACK_IMPORTED_MODULE_0__.AgrifineAgent({ onEvent: function onEvent(_ref) { var type = _ref.type, @@ -433,28 +723,27 @@ function AgRefineModule() { var last = thinkingMsg.steps[thinkingMsg.steps.length - 1]; if ((last === null || last === void 0 ? void 0 : last.type) === 'tool') last.done = true; } else if (type === 'answer') { - // Replace thinking bubble with final answer - var idx = messages.findIndex(function (m) { + var idx = agentMessages.findIndex(function (m) { return m.id === thinkingId; }); - if (idx >= 0) messages.splice(idx, 1); - messages.push({ + if (idx >= 0) agentMessages.splice(idx, 1); + agentMessages.push({ role: 'assistant', text: data }); - isRunning = false; + agentRunning = false; } else if (type === 'error') { - var _idx = messages.findIndex(function (m) { + var _idx = agentMessages.findIndex(function (m) { return m.id === thinkingId; }); - if (_idx >= 0) messages.splice(_idx, 1); - messages.push({ + if (_idx >= 0) agentMessages.splice(_idx, 1); + agentMessages.push({ role: 'error', text: data }); - isRunning = false; + agentRunning = false; } - _this3._renderMessages(container); + _this3._renderAgentMessages(container); } }); _context2.p = 2; @@ -466,58 +755,225 @@ function AgRefineModule() { case 4: _context2.p = 4; _t = _context2.v; - idx = messages.findIndex(function (m) { + idx = agentMessages.findIndex(function (m) { return m.id === thinkingId; }); - if (idx >= 0) messages.splice(idx, 1); - messages.push({ + if (idx >= 0) agentMessages.splice(idx, 1); + agentMessages.push({ role: 'error', text: _t.message }); - isRunning = false; - _this3._renderMessages(container); + agentRunning = false; + _this3._renderAgentMessages(container); case 5: return _context2.a(2); } }, _callee2, null, [[2, 4]]); }))(); }, - _renderMessages: function _renderMessages(container) { + _renderAgentMessages: function _renderAgentMessages(container) { var chat = container.querySelector('#agent-chat'); if (!chat) return; - if (messages.length === 0) { + if (agentMessages.length === 0) { var _container$querySelec2; chat.innerHTML = ''; (_container$querySelec2 = container.querySelector('#agent-suggestions')) === null || _container$querySelec2 === void 0 || _container$querySelec2.classList.remove('hidden'); return; } - chat.innerHTML = messages.map(function (msg) { + chat.innerHTML = agentMessages.map(function (msg) { if (msg.role === 'user') { - return "\n
\n
\n ".concat(escapeHtml(msg.text), "\n
\n
"); + return "
\n
\n ".concat(escapeHtml(msg.text), "\n
\n
"); } if (msg.role === 'thinking') { var _msg$steps; var steps = (_msg$steps = msg.steps) !== null && _msg$steps !== void 0 ? _msg$steps : []; - return "\n
\n ".concat(steps.map(function (step) { + return "
\n ".concat(steps.map(function (step) { if (step.type === 'status') { - return "
\n ".concat(escapeHtml(step.text), "\n
"); + return "
\n ".concat(escapeHtml(step.text), "\n
"); } if (step.type === 'tool') { - return "
\n ".concat(step.icon, "\n ").concat(step.name, "\n ").concat(step.done ? '' : '', "\n
"); + return "
\n ".concat(step.icon, "\n ").concat(step.name, "\n ").concat(step.done ? '' : '', "\n
"); } return ''; - }).join(''), "\n ").concat(steps.length === 0 ? '
Starting…
' : '', "\n
"); + }).join(''), "\n ").concat(steps.length === 0 ? '
Starting…
' : '', "\n
"); } if (msg.role === 'assistant') { - return "\n
\n
\uD83E\uDD16
\n
\n ".concat(escapeHtml(msg.text), "\n
\n
"); + return "
\n
\uD83E\uDD16
\n
".concat(escapeHtml(msg.text), "
\n
"); } if (msg.role === 'error') { - return "\n
\n \u26A0\uFE0F ".concat(escapeHtml(msg.text), "\n
"); + return "
\u26A0\uFE0F ".concat(escapeHtml(msg.text), "
"); } return ''; }).join(''); + chat.scrollTop = chat.scrollHeight; + }, + // ── Boardroom mode logic ────────────────────────────────────────────────── + _runBoardroom: function _runBoardroom(topic, container) { + var _this4 = this; + return _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee3() { + var _container$querySelec3; + var agentsToRun, priorStatements, _iterator, _step, agent, _iterator2, _step2, _loop, _t3; + return _regenerator().w(function (_context4) { + while (1) switch (_context4.p = _context4.n) { + case 0: + if (!boardRunning) { + _context4.n = 1; + break; + } + return _context4.a(2); + case 1: + boardRunning = true; + (_container$querySelec3 = container.querySelector('#board-suggestions')) === null || _container$querySelec3 === void 0 || _container$querySelec3.classList.add('hidden'); - // Scroll to bottom + // Add chair statement + boardMessages.push({ + type: 'chair', + text: topic + }); + _this4._renderBoardMessages(container); + + // Determine which agents respond + agentsToRun = boardTargetAgent ? _committee_js__WEBPACK_IMPORTED_MODULE_1__.COMMITTEE.filter(function (a) { + return a.id === boardTargetAgent; + }) : _committee_js__WEBPACK_IMPORTED_MODULE_1__.COMMITTEE; // Build context: full prior statements from this meeting for sequential passing + priorStatements = boardMessages.filter(function (m) { + return m.type === 'report' && m.text; + }).map(function (m) { + return { + name: m.agent.name, + role: m.agent.role, + text: m.text + }; + }); // Add thinking placeholders for each agent that will speak + _iterator = _createForOfIteratorHelper(agentsToRun); + try { + for (_iterator.s(); !(_step = _iterator.n()).done;) { + agent = _step.value; + boardMessages.push({ + type: 'thinking', + agentId: agent.id, + agent: agent + }); + } + } catch (err) { + _iterator.e(err); + } finally { + _iterator.f(); + } + _this4._renderBoardMessages(container); + + // Run agents sequentially so each gets the prior context + _iterator2 = _createForOfIteratorHelper(agentsToRun); + _context4.p = 2; + _loop = /*#__PURE__*/_regenerator().m(function _loop() { + var agent, msgIdx, reportMsg, _t2; + return _regenerator().w(function (_context3) { + while (1) switch (_context3.p = _context3.n) { + case 0: + agent = _step2.value; + msgIdx = boardMessages.findIndex(function (m) { + return m.type === 'thinking' && m.agentId === agent.id; + }); // Convert thinking → streaming report + reportMsg = { + type: 'report', + agent: agent, + text: '', + streaming: true + }; + if (msgIdx >= 0) boardMessages.splice(msgIdx, 1, reportMsg); + _this4._renderBoardMessages(container); + _context3.p = 1; + _context3.n = 2; + return (0,_committee_js__WEBPACK_IMPORTED_MODULE_1__.runCommitteeAgent)(agent, topic, priorStatements, function (chunk, fullText) { + reportMsg.text = fullText; + _this4._renderBoardMessages(container); + }); + case 2: + reportMsg.streaming = false; + priorStatements.push({ + name: agent.name, + role: agent.role, + text: reportMsg.text + }); + _context3.n = 4; + break; + case 3: + _context3.p = 3; + _t2 = _context3.v; + boardMessages.splice(boardMessages.indexOf(reportMsg), 1, { + type: 'error', + text: "".concat(agent.name, ": ").concat(_t2.message) + }); + case 4: + _this4._renderBoardMessages(container); + case 5: + return _context3.a(2); + } + }, _loop, null, [[1, 3]]); + }); + _iterator2.s(); + case 3: + if ((_step2 = _iterator2.n()).done) { + _context4.n = 5; + break; + } + return _context4.d(_regeneratorValues(_loop()), 4); + case 4: + _context4.n = 3; + break; + case 5: + _context4.n = 7; + break; + case 6: + _context4.p = 6; + _t3 = _context4.v; + _iterator2.e(_t3); + case 7: + _context4.p = 7; + _iterator2.f(); + return _context4.f(7); + case 8: + boardRunning = false; + + // Show target bar after the first meeting round + if (boardMessages.some(function (m) { + return m.type === 'report'; + })) { + container.querySelector('#board-target-bar').classList.remove('hidden'); + } + _this4._renderBoardMessages(container); + case 9: + return _context4.a(2); + } + }, _callee3, null, [[2, 6, 7, 8]]); + }))(); + }, + _renderBoardMessages: function _renderBoardMessages(container) { + var chat = container.querySelector('#board-chat'); + if (!chat) return; + if (boardMessages.length === 0) { + var _container$querySelec4; + chat.innerHTML = ''; + (_container$querySelec4 = container.querySelector('#board-suggestions')) === null || _container$querySelec4 === void 0 || _container$querySelec4.classList.remove('hidden'); + return; + } + chat.innerHTML = boardMessages.map(function (msg) { + if (msg.type === 'chair') { + return "\n
\n
\n
Chair \xB7 David
\n ".concat(escapeHtml(msg.text), "\n
\n
"); + } + if (msg.type === 'thinking') { + var a = msg.agent; + return "\n
\n
\n ").concat(a.emoji, "\n ").concat(a.name, "\n ").concat(a.role, "\n \n
\n
\n Reviewing data\u2026\n
\n
"); + } + if (msg.type === 'report') { + var _a = msg.agent; + return "\n
\n
\n ").concat(_a.emoji, "\n ").concat(_a.name, "\n ").concat(_a.role, "\n ").concat(msg.streaming ? '' : '', "\n
\n
\n ").concat(escapeHtml(msg.text || '…'), "\n
\n
"); + } + if (msg.type === 'error') { + return "
\u26A0\uFE0F ".concat(escapeHtml(msg.text), "
"); + } + return ''; + }).join(''); chat.scrollTop = chat.scrollHeight; } }; diff --git a/agrifine-extension/screenshots/agent_mode.png b/agrifine-extension/screenshots/agent_mode.png new file mode 100644 index 0000000000000000000000000000000000000000..1235abd7cd7497a2c5dfcb37bb1fd75de00359ff GIT binary patch literal 50877 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 literal 0 HcmV?d00001 diff --git a/agrifine-extension/src/ag-refine/committee.js b/agrifine-extension/src/ag-refine/committee.js new file mode 100644 index 0000000000..09ecdfc8e5 --- /dev/null +++ b/agrifine-extension/src/ag-refine/committee.js @@ -0,0 +1,153 @@ +/** + * The Boardroom — Multi-Agent Audit Committee + * + * Four named advisors, each with a domain-specific persona, review the same + * farm data context and report in sequence. Later agents receive a summary of + * what earlier agents said, enabling authentic cross-domain commentary. + */ +import { sessionGet, KEYS, buildContextBundle } from '../utils/storage.js'; + +const MODEL = 'claude-sonnet-4-6'; +const ANTHROPIC_URL = 'https://api.anthropic.com/v1/messages'; + +export const COMMITTEE = [ + { + id: 'financials', + name: 'Kount Kuekkens', + role: 'CFO · Financials', + emoji: '💹', + accentColor: '#d97706', + borderStyle: 'border-l-[3px]', + borderColor: '#d97706', + persona: `You are Kount Kuekkens, a retired agricultural economist now serving as CFO for this farm operation. You speak with a spiraling rhetorical style — you begin broad, drift into economic theory or historical context, but always land on concrete "Dairy Moneyball" math that actually matters. + +Your domain: Income Over Feed Cost (IOFC), commodity price impacts on margins, feed efficiency ratios, processor quality premium/penalty thresholds, cash flow position, budget variances, and the financial consequences of operational data errors. + +When you spot data problems, quantify the financial blindspot they create. When you see opportunities, express them in dollar terms. You are candid about when you are "spiraling" into uncertainty vs. when you have hard numbers. You occasionally reference obscure economic principles before getting to the point. + +Report in 3–4 paragraphs. Be specific — name dollar figures, percentages, and cite the data points you are drawing from.`, + }, + { + id: 'crops', + name: 'Rolf Forage', + role: 'Agronomist · Crops', + emoji: '🌾', + accentColor: '#16a34a', + borderColor: '#16a34a', + persona: `You are Rolf Forage (pronounced "For-ahh-juz"), a fiercely opinionated agronomist and crops director. You do not care about spreadsheets or financial models — you care about what is actually in the field and the bunker right now. + +Your domain: forage quality (dry matter, NDF, fiber digestibility), silage inventory and fermentation integrity, harvest timing windows, field conditions (soil type, drainage, compaction), cover crop programs, nutrient cycling, and input scheduling. + +You are demanding and direct. If the data shows a crop problem that will compromise feed quality, you say so loudly and insist it be corrected immediately — you do not sugarcoat risk to protect someone's budget. You will call out the financial team for cutting corners that ultimately cost more in lost production. You speak in practical, field-level language. + +Report in 3–4 paragraphs. Be opinionated and specific about what needs to happen and when.`, + }, + { + id: 'herd', + name: 'Dr. Vera Hest', + role: 'Chief Veterinarian · Herd Health', + emoji: '🐄', + accentColor: '#60a5fa', + borderColor: '#60a5fa', + persona: `You are Dr. Vera Hest, a sharp-witted, data-driven veterinarian and herd health director. You value biological metrics and animal welfare above all else — and you will challenge any department that proposes to compromise herd health in the name of cost savings or operational convenience. + +Your domain: Somatic Cell Count (SCC) trends and penalty risk, Dry Matter Intake (DMI) per cow, Body Condition Score (BCS), transition cow health, Temperature-Humidity Index (THI) and heat stress protocol, milk component trends (fat, protein), reproductive performance, and disease incidence (ketosis, mastitis, lameness, displaced abomasum). + +You connect biological metrics to production outcomes — a BCS over 3.75 at calving means dystocia and ketosis next month; a THI of 86 means DMI drops 10–15% and milk yield follows within 48 hours. You are precise with thresholds, not vague. You speak clinically but translate findings for the group when needed. + +Report in 3–4 paragraphs. Be incisive. Cite specific thresholds and explain their downstream consequences.`, + }, + { + id: 'personnel', + name: 'Marla Shift', + role: 'Operations Manager · Personnel', + emoji: '📋', + accentColor: '#94a3b8', + borderColor: '#94a3b8', + persona: `You are Marla Shift, the operations-hardened manager who oversees labor, personnel scheduling, equipment maintenance, and day-to-day execution. You are the "reality check" of the boardroom. + +Your domain: labor availability and shift coverage, overtime costs and crew fatigue, equipment uptime and maintenance backlogs, safety compliance, training gaps, and operational root causes of data errors or production misses. + +When the other advisors make demands — Rolf needs an early harvest crew, Vera wants manual pen checks every two hours, Kount wants a new validation system built by Friday — you translate those demands into actual execution requirements: how many people, how many hours, what it costs, and what else will be delayed or skipped to make it happen. + +You provide honest operational explanations (not excuses) for why things went wrong: mechanical failures, staffing gaps during peak periods, training slips under pressure. You are pragmatic, occasionally exasperated, and very good at finding workarounds under real-world constraints. + +Report in 3–4 paragraphs. Be concrete about labor, time, and resource constraints.`, + }, +]; + +/** + * Run a single committee agent and stream their response. + * priorStatements: array of { name, role, text } from agents who already spoke. + * onChunk: called with partial text as it streams in. + */ +export async function runCommitteeAgent(agent, topic, priorStatements, onChunk) { + const apiKey = await sessionGet(KEYS.API_KEY); + if (!apiKey) throw new Error('No API key set — open ⚙ Settings to add your Anthropic key.'); + + const contextBundle = await buildContextBundle(); + + const priorContext = priorStatements.length > 0 + ? `\n\n── PRIOR STATEMENTS FROM YOUR COLLEAGUES ──\n${priorStatements.map( + (s) => `${s.name} (${s.role}):\n${s.text}` + ).join('\n\n─────────────────────\n\n')}` + : ''; + + const systemPrompt = `${agent.persona} + +── FARM DATA CONTEXT ── +${contextBundle}${priorContext} + +You are presenting your department report at the weekly audit boardroom. Address the meeting topic directly from your domain's perspective. If colleagues have already spoken, you may reference or push back on their points where they intersect with your domain.`; + + const res = await fetch(ANTHROPIC_URL, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'x-api-key': apiKey, + 'anthropic-version': '2023-06-01', + 'anthropic-dangerous-direct-browser-access': 'true', + }, + body: JSON.stringify({ + model: MODEL, + max_tokens: 1024, + stream: true, + system: systemPrompt, + messages: [{ role: 'user', content: topic }], + }), + }); + + if (!res.ok) { + const text = await res.text(); + throw new Error(`API ${res.status}: ${text}`); + } + + // Stream SSE response + const reader = res.body.getReader(); + const decoder = new TextDecoder(); + let fullText = ''; + let buffer = ''; + + while (true) { + const { done, value } = await reader.read(); + if (done) break; + buffer += decoder.decode(value, { stream: true }); + const lines = buffer.split('\n'); + buffer = lines.pop() ?? ''; + + for (const line of lines) { + if (!line.startsWith('data: ')) continue; + const payload = line.slice(6).trim(); + if (payload === '[DONE]') continue; + try { + const evt = JSON.parse(payload); + if (evt.type === 'content_block_delta' && evt.delta?.type === 'text_delta') { + fullText += evt.delta.text; + onChunk(evt.delta.text, fullText); + } + } catch (_) {} + } + } + + return fullText; +} diff --git a/agrifine-extension/src/ag-refine/index.js b/agrifine-extension/src/ag-refine/index.js index 718eb07d4e..fed6ae1f5b 100644 --- a/agrifine-extension/src/ag-refine/index.js +++ b/agrifine-extension/src/ag-refine/index.js @@ -1,4 +1,5 @@ import { AgrifineAgent } from './agent.js'; +import { COMMITTEE, runCommitteeAgent } from './committee.js'; const TOOL_ICONS = { get_reading_list: '📖', @@ -16,7 +17,7 @@ const TOOL_ICONS = { update_farm_memory: '💾', }; -const SUGGESTED_PROMPTS = [ +const AGENT_PROMPTS = [ 'Review all my farm data and build a farm memory summary', 'What are my current field conditions and harvest windows?', 'What risks or opportunities do you see across my operation?', @@ -24,111 +25,264 @@ const SUGGESTED_PROMPTS = [ 'Export my reading list and field profiles to CSV', ]; +const BOARDROOM_PROMPTS = [ + 'Weekly operations audit — all departments, give me your status', + 'We have a data integrity issue — assess the impact by department', + 'Heat stress event incoming — what does each department need?', + 'Review all farm data and identify the single biggest risk per department', + 'What is the biggest point of contention between departments right now?', +]; + export function AgRefineModule() { - let messages = []; - let isRunning = false; + let mode = 'agent'; // 'agent' | 'boardroom' + + // Agent mode state + let agentMessages = []; + let agentRunning = false; + + // Boardroom mode state + let boardMessages = []; // { type: 'topic'|'report'|'chair'|'thinking', ... } + let boardRunning = false; + let boardTargetAgent = null; // null = all, or agent id for targeted response return { id: 'ag-refine', label: 'AgriAgent', async render(container) { - container.innerHTML = ` + container.innerHTML = this._html(); + this._bindEvents(container); + this._switchMode(mode, container); + }, + + _html() { + return `
- -
-
- 🤖 -

AgriAgent

- AI Agent -
-

Multi-step reasoning over all your farm data

+ +
+ +
- -
- - -
-

Try asking…

-
- ${SUGGESTED_PROMPTS.map((p) => ` - `).join('')} + + - -
-
- - + + +
`; + }, - this._bindEvents(container); - this._renderMessages(container); + _switchMode(newMode, container) { + mode = newMode; + container.querySelector('#panel-agent').classList.toggle('hidden', mode !== 'agent'); + container.querySelector('#panel-boardroom').classList.toggle('hidden', mode !== 'boardroom'); + + container.querySelectorAll('.mode-btn').forEach((btn) => { + const active = btn.dataset.mode === mode; + btn.style.color = active ? '#22c55e' : '#3d4f66'; + btn.style.borderBottom = active ? '2px solid #22c55e' : '2px solid transparent'; + }); }, _bindEvents(container) { - const input = container.querySelector('#agent-input'); - const sendBtn = container.querySelector('#agent-send'); + // Mode toggle + container.querySelectorAll('.mode-btn').forEach((btn) => { + btn.addEventListener('click', () => this._switchMode(btn.dataset.mode, container)); + }); + + // ── Agent mode ── + const agentInput = container.querySelector('#agent-input'); + const agentSend = container.querySelector('#agent-send'); - const send = () => { - const text = input.value.trim(); - if (!text || isRunning) return; - input.value = ''; + const sendAgent = () => { + const text = agentInput.value.trim(); + if (!text || agentRunning) return; + agentInput.value = ''; this._runAgent(text, container); }; - sendBtn.addEventListener('click', send); - input.addEventListener('keydown', (e) => { - if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); send(); } + agentSend.addEventListener('click', sendAgent); + agentInput.addEventListener('keydown', (e) => { + if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); sendAgent(); } }); - container.querySelectorAll('.suggest-btn').forEach((btn) => { + container.querySelectorAll('.agent-suggest-btn').forEach((btn) => { btn.addEventListener('click', () => { - input.value = btn.textContent.trim(); - send(); + agentInput.value = btn.textContent.trim(); + sendAgent(); }); }); container.querySelector('#agent-clear').addEventListener('click', () => { - messages = []; - isRunning = false; - this._renderMessages(container); + agentMessages = []; + agentRunning = false; + this._renderAgentMessages(container); + }); + + // ── Boardroom mode ── + const boardInput = container.querySelector('#board-input'); + const boardSend = container.querySelector('#board-send'); + + const sendBoard = () => { + const text = boardInput.value.trim(); + if (!text || boardRunning) return; + boardInput.value = ''; + this._runBoardroom(text, container); + }; + + boardSend.addEventListener('click', sendBoard); + boardInput.addEventListener('keydown', (e) => { + if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); sendBoard(); } + }); + + container.querySelectorAll('.board-suggest-btn').forEach((btn) => { + btn.addEventListener('click', () => { + boardInput.value = btn.textContent.trim(); + sendBoard(); + }); + }); + + container.querySelector('#board-clear').addEventListener('click', () => { + boardMessages = []; + boardRunning = false; + boardTargetAgent = null; + container.querySelector('#board-target-bar').classList.add('hidden'); + this._renderBoardMessages(container); + }); + + // Target agent selector + container.querySelectorAll('.target-btn').forEach((btn) => { + btn.addEventListener('click', () => { + boardTargetAgent = btn.dataset.target === 'all' ? null : btn.dataset.target; + container.querySelectorAll('.target-btn').forEach((b) => { + b.classList.toggle('active-target', b.dataset.target === (boardTargetAgent ?? 'all')); + b.style.borderColor = b.classList.contains('active-target') ? '#22c55e' : ''; + if (!b.classList.contains('active-target')) b.style.borderColor = '#253047'; + }); + }); }); }, + // ── Agent mode logic ────────────────────────────────────────────────────── + async _runAgent(userText, container) { - if (isRunning) return; - isRunning = true; + if (agentRunning) return; + agentRunning = true; - // Hide suggestions container.querySelector('#agent-suggestions')?.classList.add('hidden'); + agentMessages.push({ role: 'user', text: userText }); + this._renderAgentMessages(container); - // Add user message - messages.push({ role: 'user', text: userText }); - this._renderMessages(container); - - // Thinking placeholder const thinkingId = `thinking_${Date.now()}`; - messages.push({ role: 'thinking', id: thinkingId, steps: [] }); - this._renderMessages(container); + agentMessages.push({ role: 'thinking', id: thinkingId, steps: [] }); + this._renderAgentMessages(container); - const thinkingMsg = messages[messages.length - 1]; + const thinkingMsg = agentMessages[agentMessages.length - 1]; const agent = new AgrifineAgent({ onEvent: ({ type, data }) => { @@ -145,96 +299,209 @@ export function AgRefineModule() { const last = thinkingMsg.steps[thinkingMsg.steps.length - 1]; if (last?.type === 'tool') last.done = true; } else if (type === 'answer') { - // Replace thinking bubble with final answer - const idx = messages.findIndex((m) => m.id === thinkingId); - if (idx >= 0) messages.splice(idx, 1); - messages.push({ role: 'assistant', text: data }); - isRunning = false; + const idx = agentMessages.findIndex((m) => m.id === thinkingId); + if (idx >= 0) agentMessages.splice(idx, 1); + agentMessages.push({ role: 'assistant', text: data }); + agentRunning = false; } else if (type === 'error') { - const idx = messages.findIndex((m) => m.id === thinkingId); - if (idx >= 0) messages.splice(idx, 1); - messages.push({ role: 'error', text: data }); - isRunning = false; + const idx = agentMessages.findIndex((m) => m.id === thinkingId); + if (idx >= 0) agentMessages.splice(idx, 1); + agentMessages.push({ role: 'error', text: data }); + agentRunning = false; } - this._renderMessages(container); + this._renderAgentMessages(container); }, }); try { await agent.run(userText); } catch (err) { - const idx = messages.findIndex((m) => m.id === thinkingId); - if (idx >= 0) messages.splice(idx, 1); - messages.push({ role: 'error', text: err.message }); - isRunning = false; - this._renderMessages(container); + const idx = agentMessages.findIndex((m) => m.id === thinkingId); + if (idx >= 0) agentMessages.splice(idx, 1); + agentMessages.push({ role: 'error', text: err.message }); + agentRunning = false; + this._renderAgentMessages(container); } }, - _renderMessages(container) { + _renderAgentMessages(container) { const chat = container.querySelector('#agent-chat'); if (!chat) return; - if (messages.length === 0) { + if (agentMessages.length === 0) { chat.innerHTML = ''; container.querySelector('#agent-suggestions')?.classList.remove('hidden'); return; } - chat.innerHTML = messages.map((msg) => { + chat.innerHTML = agentMessages.map((msg) => { if (msg.role === 'user') { + return `
+
+ ${escapeHtml(msg.text)} +
+
`; + } + if (msg.role === 'thinking') { + const steps = msg.steps ?? []; + return `
+ ${steps.map((step) => { + if (step.type === 'status') { + return `
+ ${escapeHtml(step.text)} +
`; + } + if (step.type === 'tool') { + return `
+ ${step.icon} + ${step.name} + ${step.done ? '' : ''} +
`; + } + return ''; + }).join('')} + ${steps.length === 0 ? '
Starting…
' : ''} +
`; + } + if (msg.role === 'assistant') { + return `
+
🤖
+
${escapeHtml(msg.text)}
+
`; + } + if (msg.role === 'error') { + return `
⚠️ ${escapeHtml(msg.text)}
`; + } + return ''; + }).join(''); + + chat.scrollTop = chat.scrollHeight; + }, + + // ── Boardroom mode logic ────────────────────────────────────────────────── + + async _runBoardroom(topic, container) { + if (boardRunning) return; + boardRunning = true; + + container.querySelector('#board-suggestions')?.classList.add('hidden'); + + // Add chair statement + boardMessages.push({ type: 'chair', text: topic }); + this._renderBoardMessages(container); + + // Determine which agents respond + const agentsToRun = boardTargetAgent + ? COMMITTEE.filter((a) => a.id === boardTargetAgent) + : COMMITTEE; + + // Build context: full prior statements from this meeting for sequential passing + const priorStatements = boardMessages + .filter((m) => m.type === 'report' && m.text) + .map((m) => ({ name: m.agent.name, role: m.agent.role, text: m.text })); + + // Add thinking placeholders for each agent that will speak + for (const agent of agentsToRun) { + boardMessages.push({ type: 'thinking', agentId: agent.id, agent }); + } + this._renderBoardMessages(container); + + // Run agents sequentially so each gets the prior context + for (const agent of agentsToRun) { + const msgIdx = boardMessages.findIndex( + (m) => m.type === 'thinking' && m.agentId === agent.id + ); + + // Convert thinking → streaming report + const reportMsg = { type: 'report', agent, text: '', streaming: true }; + if (msgIdx >= 0) boardMessages.splice(msgIdx, 1, reportMsg); + this._renderBoardMessages(container); + + try { + await runCommitteeAgent(agent, topic, priorStatements, (chunk, fullText) => { + reportMsg.text = fullText; + this._renderBoardMessages(container); + }); + reportMsg.streaming = false; + priorStatements.push({ name: agent.name, role: agent.role, text: reportMsg.text }); + } catch (err) { + boardMessages.splice(boardMessages.indexOf(reportMsg), 1, { + type: 'error', + text: `${agent.name}: ${err.message}`, + }); + } + this._renderBoardMessages(container); + } + + boardRunning = false; + + // Show target bar after the first meeting round + if (boardMessages.some((m) => m.type === 'report')) { + container.querySelector('#board-target-bar').classList.remove('hidden'); + } + this._renderBoardMessages(container); + }, + + _renderBoardMessages(container) { + const chat = container.querySelector('#board-chat'); + if (!chat) return; + + if (boardMessages.length === 0) { + chat.innerHTML = ''; + container.querySelector('#board-suggestions')?.classList.remove('hidden'); + return; + } + + chat.innerHTML = boardMessages.map((msg) => { + if (msg.type === 'chair') { return `
-
+
+
Chair · David
${escapeHtml(msg.text)}
`; } - if (msg.role === 'thinking') { - const steps = msg.steps ?? []; + if (msg.type === 'thinking') { + const a = msg.agent; return ` -
- ${steps.map((step) => { - if (step.type === 'status') { - return `
- ${escapeHtml(step.text)} -
`; - } - if (step.type === 'tool') { - return `
- ${step.icon} - ${step.name} - ${step.done ? '' : ''} -
`; - } - return ''; - }).join('')} - ${steps.length === 0 ? '
Starting…
' : ''} +
+
+ ${a.emoji} + ${a.name} + ${a.role} + +
+
+ Reviewing data… +
`; } - if (msg.role === 'assistant') { + if (msg.type === 'report') { + const a = msg.agent; return ` -
-
🤖
-
- ${escapeHtml(msg.text)} +
+
+ ${a.emoji} + ${a.name} + ${a.role} + ${msg.streaming ? '' : ''} +
+
+ ${escapeHtml(msg.text || '…')}
`; } - if (msg.role === 'error') { - return ` -
- ⚠️ ${escapeHtml(msg.text)} -
`; + if (msg.type === 'error') { + return `
⚠️ ${escapeHtml(msg.text)}
`; } return ''; }).join(''); - // Scroll to bottom chat.scrollTop = chat.scrollHeight; }, }; From c653ef7e3359dd6123a83fd7286d3917549a12bc Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 27 Jun 2026 20:12:09 +0000 Subject: [PATCH 15/17] Include updated sidebar.css dist artifact Co-Authored-By: Claude Sonnet 4.6 Claude-Session: https://claude.ai/code/session_01KBD2dN2KEjzz3UQFa9hEpu --- agrifine-extension/dist/sidebar.css | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/agrifine-extension/dist/sidebar.css b/agrifine-extension/dist/sidebar.css index b8132afe63..270786e1f6 100644 --- a/agrifine-extension/dist/sidebar.css +++ b/agrifine-extension/dist/sidebar.css @@ -567,6 +567,9 @@ video { .ml-auto { margin-left: auto; } +.mr-0\.5 { + margin-right: 0.125rem; +} .mt-0\.5 { margin-top: 0.125rem; } @@ -723,6 +726,9 @@ video { .justify-between { justify-content: space-between; } +.gap-1 { + gap: 0.25rem; +} .gap-1\.5 { gap: 0.375rem; } @@ -767,6 +773,9 @@ video { margin-top: calc(0.75rem * calc(1 - var(--tw-space-y-reverse))); margin-bottom: calc(0.75rem * var(--tw-space-y-reverse)); } +.self-center { + align-self: center; +} .overflow-hidden { overflow: hidden; } @@ -811,6 +820,9 @@ video { .border-b { border-bottom-width: 1px; } +.border-l-\[3px\] { + border-left-width: 3px; +} .border-t { border-top-width: 1px; } @@ -923,6 +935,9 @@ video { padding-top: 0.75rem; padding-bottom: 0.75rem; } +.pb-1 { + padding-bottom: 0.25rem; +} .pb-1\.5 { padding-bottom: 0.375rem; } @@ -935,12 +950,12 @@ video { .pt-2 { padding-top: 0.5rem; } +.pt-2\.5 { + padding-top: 0.625rem; +} .pt-3 { padding-top: 0.75rem; } -.pt-4 { - padding-top: 1rem; -} .text-left { text-align: left; } @@ -1015,6 +1030,10 @@ video { .tracking-widest { letter-spacing: 0.1em; } +.text-agri-200 { + --tw-text-opacity: 1; + color: rgb(187 247 208 / var(--tw-text-opacity, 1)); +} .text-agri-400 { --tw-text-opacity: 1; color: rgb(74 222 128 / var(--tw-text-opacity, 1)); From 82e84ddef76f3c40cfcbf5eba7f83091ec94e06c Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 27 Jun 2026 20:20:39 +0000 Subject: [PATCH 16/17] feat(agrifine): rewrite AG-Refine bridge to use REST API + enrich committee personas Replace localStorage-scraping approach with proper REST API calls. The bridge now injects async fetch functions into the AG-Refine tab's page context so session cookies work without any CORS configuration on the server. syncFromAgRefine() now calls /api/fields/, /api/scales/tickets/all, /api/intelligence/lab-samples, and /api/harvest/plans simultaneously. WeighTicket records (DM%, protein, commodity, harvest label) are mapped to per-field harvest records; NIR/lab samples are mapped to per-field labSamples arrays and surfaced in the field profile expanded card. pushToAgRefine() creates new fields via POST /api/fields/ and back-fills _agRefineId on success so subsequent pulls correctly merge rather than duplicate. buildContextBundle in storage.js now includes latest lab sample quality metrics (DM%, NDF%, RFV, NEL) in the AI context for each field. Committee agent personas enriched with AG-Refine-specific domain knowledge: Kount reads ticket DM% and integrity scores for cost-per-ton math; Rolf reads lab NDF/ADF/RFV against thresholds and harvest calendar cut timing; Dr. Vera connects forage DM% on tickets to intake prediction and ketosis risk; Marla reads operation cycle times and driver assignment logs for throughput analysis. Co-Authored-By: Claude Sonnet 4.6 Claude-Session: https://claude.ai/code/session_01KBD2dN2KEjzz3UQFa9hEpu --- agrifine-extension/dist/background.js | 863 +++++++++++------ agrifine-extension/dist/sidebar.css | 3 + agrifine-extension/dist/sidebar.js | 890 ++++++++++++------ agrifine-extension/src/ag-refine/committee.js | 10 +- .../src/modules/field-profile/index.js | 39 +- .../src/utils/agrefine-bridge.js | 348 ++++--- agrifine-extension/src/utils/storage.js | 16 +- 7 files changed, 1426 insertions(+), 743 deletions(-) diff --git a/agrifine-extension/dist/background.js b/agrifine-extension/dist/background.js index 342881462d..5994aef004 100644 --- a/agrifine-extension/dist/background.js +++ b/agrifine-extension/dist/background.js @@ -17,33 +17,34 @@ __webpack_require__.r(__webpack_exports__); /* harmony export */ syncFromAgRefine: () => (/* binding */ syncFromAgRefine) /* harmony export */ }); /* harmony import */ var _storage_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./storage.js */ "./src/utils/storage.js"); +function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); } function _regeneratorValues(e) { if (null != e) { var t = e["function" == typeof Symbol && Symbol.iterator || "@@iterator"], r = 0; if (t) return t.call(e); if ("function" == typeof e.next) return e; if (!isNaN(e.length)) return { next: function next() { return e && r >= e.length && (e = void 0), { value: e && e[r++], done: !e }; } }; } throw new TypeError(_typeof(e) + " is not iterable"); } -function _regenerator() { /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/babel/babel/blob/main/packages/babel-helpers/LICENSE */ var e, t, r = "function" == typeof Symbol ? Symbol : {}, n = r.iterator || "@@iterator", o = r.toStringTag || "@@toStringTag"; function i(r, n, o, i) { var c = n && n.prototype instanceof Generator ? n : Generator, u = Object.create(c.prototype); return _regeneratorDefine2(u, "_invoke", function (r, n, o) { var i, c, u, f = 0, p = o || [], y = !1, G = { p: 0, n: 0, v: e, a: d, f: d.bind(e, 4), d: function d(t, r) { return i = t, c = 0, u = e, G.n = r, a; } }; function d(r, n) { for (c = r, u = n, t = 0; !y && f && !o && t < p.length; t++) { var o, i = p[t], d = G.p, l = i[2]; r > 3 ? (o = l === n) && (u = i[(c = i[4]) ? 5 : (c = 3, 3)], i[4] = i[5] = e) : i[0] <= d && ((o = r < 2 && d < i[1]) ? (c = 0, G.v = n, G.n = i[1]) : d < l && (o = r < 3 || i[0] > n || n > l) && (i[4] = r, i[5] = n, G.n = l, c = 0)); } if (o || r > 1) return a; throw y = !0, n; } return function (o, p, l) { if (f > 1) throw TypeError("Generator is already running"); for (y && 1 === p && d(p, l), c = p, u = l; (t = c < 2 ? e : u) || !y;) { i || (c ? c < 3 ? (c > 1 && (G.n = -1), d(c, u)) : G.n = u : G.v = u); try { if (f = 2, i) { if (c || (o = "next"), t = i[o]) { if (!(t = t.call(i, u))) throw TypeError("iterator result is not an object"); if (!t.done) return t; u = t.value, c < 2 && (c = 0); } else 1 === c && (t = i["return"]) && t.call(i), c < 2 && (u = TypeError("The iterator does not provide a '" + o + "' method"), c = 1); i = e; } else if ((t = (y = G.n < 0) ? u : r.call(n, G)) !== a) break; } catch (t) { i = e, c = 1, u = t; } finally { f = 1; } } return { value: t, done: y }; }; }(r, o, i), !0), u; } var a = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} t = Object.getPrototypeOf; var c = [][n] ? t(t([][n]())) : (_regeneratorDefine2(t = {}, n, function () { return this; }), t), u = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(c); function f(e) { return Object.setPrototypeOf ? Object.setPrototypeOf(e, GeneratorFunctionPrototype) : (e.__proto__ = GeneratorFunctionPrototype, _regeneratorDefine2(e, o, "GeneratorFunction")), e.prototype = Object.create(u), e; } return GeneratorFunction.prototype = GeneratorFunctionPrototype, _regeneratorDefine2(u, "constructor", GeneratorFunctionPrototype), _regeneratorDefine2(GeneratorFunctionPrototype, "constructor", GeneratorFunction), GeneratorFunction.displayName = "GeneratorFunction", _regeneratorDefine2(GeneratorFunctionPrototype, o, "GeneratorFunction"), _regeneratorDefine2(u), _regeneratorDefine2(u, o, "Generator"), _regeneratorDefine2(u, n, function () { return this; }), _regeneratorDefine2(u, "toString", function () { return "[object Generator]"; }), (_regenerator = function _regenerator() { return { w: i, m: f }; })(); } -function _regeneratorDefine2(e, r, n, t) { var i = Object.defineProperty; try { i({}, "", {}); } catch (e) { i = 0; } _regeneratorDefine2 = function _regeneratorDefine(e, r, n, t) { function o(r, n) { _regeneratorDefine2(e, r, function (e) { return this._invoke(r, n, e); }); } r ? i ? i(e, r, { value: n, enumerable: !t, configurable: !t, writable: !t }) : e[r] = n : (o("next", 0), o("throw", 1), o("return", 2)); }, _regeneratorDefine2(e, r, n, t); } +function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } +function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } +function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; } +function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; } +function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t["return"] || t["return"](); } finally { if (u) throw o; } } }; } -function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); } function _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); } function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } } function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; } function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t["return"] && (u = t["return"](), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } } function _arrayWithHoles(r) { if (Array.isArray(r)) return r; } -function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } -function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } -function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; } -function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; } -function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } +function _regenerator() { /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/babel/babel/blob/main/packages/babel-helpers/LICENSE */ var e, t, r = "function" == typeof Symbol ? Symbol : {}, n = r.iterator || "@@iterator", o = r.toStringTag || "@@toStringTag"; function i(r, n, o, i) { var c = n && n.prototype instanceof Generator ? n : Generator, u = Object.create(c.prototype); return _regeneratorDefine2(u, "_invoke", function (r, n, o) { var i, c, u, f = 0, p = o || [], y = !1, G = { p: 0, n: 0, v: e, a: d, f: d.bind(e, 4), d: function d(t, r) { return i = t, c = 0, u = e, G.n = r, a; } }; function d(r, n) { for (c = r, u = n, t = 0; !y && f && !o && t < p.length; t++) { var o, i = p[t], d = G.p, l = i[2]; r > 3 ? (o = l === n) && (u = i[(c = i[4]) ? 5 : (c = 3, 3)], i[4] = i[5] = e) : i[0] <= d && ((o = r < 2 && d < i[1]) ? (c = 0, G.v = n, G.n = i[1]) : d < l && (o = r < 3 || i[0] > n || n > l) && (i[4] = r, i[5] = n, G.n = l, c = 0)); } if (o || r > 1) return a; throw y = !0, n; } return function (o, p, l) { if (f > 1) throw TypeError("Generator is already running"); for (y && 1 === p && d(p, l), c = p, u = l; (t = c < 2 ? e : u) || !y;) { i || (c ? c < 3 ? (c > 1 && (G.n = -1), d(c, u)) : G.n = u : G.v = u); try { if (f = 2, i) { if (c || (o = "next"), t = i[o]) { if (!(t = t.call(i, u))) throw TypeError("iterator result is not an object"); if (!t.done) return t; u = t.value, c < 2 && (c = 0); } else 1 === c && (t = i["return"]) && t.call(i), c < 2 && (u = TypeError("The iterator does not provide a '" + o + "' method"), c = 1); i = e; } else if ((t = (y = G.n < 0) ? u : r.call(n, G)) !== a) break; } catch (t) { i = e, c = 1, u = t; } finally { f = 1; } } return { value: t, done: y }; }; }(r, o, i), !0), u; } var a = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} t = Object.getPrototypeOf; var c = [][n] ? t(t([][n]())) : (_regeneratorDefine2(t = {}, n, function () { return this; }), t), u = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(c); function f(e) { return Object.setPrototypeOf ? Object.setPrototypeOf(e, GeneratorFunctionPrototype) : (e.__proto__ = GeneratorFunctionPrototype, _regeneratorDefine2(e, o, "GeneratorFunction")), e.prototype = Object.create(u), e; } return GeneratorFunction.prototype = GeneratorFunctionPrototype, _regeneratorDefine2(u, "constructor", GeneratorFunctionPrototype), _regeneratorDefine2(GeneratorFunctionPrototype, "constructor", GeneratorFunction), GeneratorFunction.displayName = "GeneratorFunction", _regeneratorDefine2(GeneratorFunctionPrototype, o, "GeneratorFunction"), _regeneratorDefine2(u), _regeneratorDefine2(u, o, "Generator"), _regeneratorDefine2(u, n, function () { return this; }), _regeneratorDefine2(u, "toString", function () { return "[object Generator]"; }), (_regenerator = function _regenerator() { return { w: i, m: f }; })(); } +function _regeneratorDefine2(e, r, n, t) { var i = Object.defineProperty; try { i({}, "", {}); } catch (e) { i = 0; } _regeneratorDefine2 = function _regeneratorDefine(e, r, n, t) { function o(r, n) { _regeneratorDefine2(e, r, function (e) { return this._invoke(r, n, e); }); } r ? i ? i(e, r, { value: n, enumerable: !t, configurable: !t, writable: !t }) : e[r] = n : (o("next", 0), o("throw", 1), o("return", 2)); }, _regeneratorDefine2(e, r, n, t); } function asyncGeneratorStep(n, t, e, r, o, a, c) { try { var i = n[a](c), u = i.value; } catch (n) { return void e(n); } i.done ? t(u) : Promise.resolve(u).then(r, o); } function _asyncToGenerator(n) { return function () { var t = this, e = arguments; return new Promise(function (r, o) { var a = n.apply(t, e); function _next(n) { asyncGeneratorStep(a, r, o, _next, _throw, "next", n); } function _throw(n) { asyncGeneratorStep(a, r, o, _next, _throw, "throw", n); } _next(void 0); }); }; } /** * AG-Refine Sister-App Bridge * - * Detects an open AG-Refine tab, pulls field and output data from its - * localStorage/sessionStorage, and maps it into Agrifine field profiles. + * Calls AG-Refine's REST API through the tab's own page context so session + * cookies work without any CORS setup on the server. Requires AG-Refine to + * be open in a browser tab and the user to be logged in there. * - * AG-Refine tab detection: any tab whose URL matches a configurable pattern - * (default: localhost:* OR any URL containing "ag-refine" or "agrefine"). - * Set the URL in Settings > AG-Refine URL to pin it to a specific origin. + * Tab detection: matches any tab whose URL starts with the configured base URL + * (default: localhost:* or any URL containing "ag-refine" / "agrefine"). + * Set Settings → AG-Refine URL to pin it to a specific origin. */ @@ -151,353 +152,626 @@ function tabMatchesAgRefine(tab, configuredUrl) { var u = tab.url.toLowerCase(); return u.includes('ag-refine') || u.includes('agrefine') || u.startsWith('http://localhost') || u.startsWith('http://127.0.0.1'); } - -// Injected into the AG-Refine tab — reads all storage and DOM hints -function scrapeAgRefineTab() { - var out = { - localStorage: {}, - sessionStorage: {}, - domHints: {} - }; - for (var i = 0; i < localStorage.length; i++) { - var k = localStorage.key(i); - try { - out.localStorage[k] = JSON.parse(localStorage.getItem(k)); - } catch (_) { - out.localStorage[k] = localStorage.getItem(k); - } - } - for (var _i = 0; _i < sessionStorage.length; _i++) { - var _k = sessionStorage.key(_i); - try { - out.sessionStorage[_k] = JSON.parse(sessionStorage.getItem(_k)); - } catch (_) { - out.sessionStorage[_k] = sessionStorage.getItem(_k); - } - } - - // Pull field-name-like text from the DOM as a fallback hint - var fieldEls = document.querySelectorAll('[data-field],[data-name],[data-id]'); - fieldEls.forEach(function (el) { - var _ref, _el$dataset$field, _el$textContent; - var id = (_ref = (_el$dataset$field = el.dataset.field) !== null && _el$dataset$field !== void 0 ? _el$dataset$field : el.dataset.id) !== null && _ref !== void 0 ? _ref : el.dataset.name; - if (id) out.domHints[id] = ((_el$textContent = el.textContent) !== null && _el$textContent !== void 0 ? _el$textContent : '').trim().slice(0, 200); - }); - return out; -} - -/** - * Map raw AG-Refine storage dump to Agrifine field profile shape. - * Tries common key patterns used by React/Next.js ag apps. - */ -function extractFields(raw) { - var all = _objectSpread(_objectSpread({}, raw.localStorage), raw.sessionStorage); - var candidates = []; - for (var _i2 = 0, _Object$entries = Object.entries(all); _i2 < _Object$entries.length; _i2++) { - var _Object$entries$_i = _slicedToArray(_Object$entries[_i2], 2), - key = _Object$entries$_i[0], - val = _Object$entries$_i[1]; - var k = key.toLowerCase(); - if (!k.includes('field') && !k.includes('load') && !k.includes('farm') && !k.includes('plot')) continue; - var arr = Array.isArray(val) ? val : val && _typeof(val) === 'object' ? [val] : null; - if (!arr) continue; - var _iterator = _createForOfIteratorHelper(arr), - _step; - try { - for (_iterator.s(); !(_step = _iterator.n()).done;) { - var _ref2, _ref3, _ref4, _ref5, _item$name, _ref6, _item$id, _ref7, _ref8, _item$cluId, _ref9, _ref0, _item$acres, _ref1, _ref10, _item$soilType, _ref11, _item$lat, _item$coordinates, _ref12, _ref13, _ref14, _item$lon, _item$coordinates2, _item$coordinates3, _ref15, _ref16, _item$notes, _item$cropHistory, _item$cropHistory2, _item$harvests, _item$harvests2, _item$carbonPotential, _ref17, _item$createdAt; - var item = _step.value; - if (!item || _typeof(item) !== 'object') continue; - var name = (_ref2 = (_ref3 = (_ref4 = (_ref5 = (_item$name = item.name) !== null && _item$name !== void 0 ? _item$name : item.fieldName) !== null && _ref5 !== void 0 ? _ref5 : item.field_name) !== null && _ref4 !== void 0 ? _ref4 : item.title) !== null && _ref3 !== void 0 ? _ref3 : item.label) !== null && _ref2 !== void 0 ? _ref2 : null; - if (!name) continue; - candidates.push({ - id: "agr_".concat((_ref6 = (_item$id = item.id) !== null && _item$id !== void 0 ? _item$id : item.fieldId) !== null && _ref6 !== void 0 ? _ref6 : Date.now(), "_").concat(Math.random().toString(36).slice(2, 6)), - name: String(name), - cluId: (_ref7 = (_ref8 = (_item$cluId = item.cluId) !== null && _item$cluId !== void 0 ? _item$cluId : item.clu_id) !== null && _ref8 !== void 0 ? _ref8 : item.clu) !== null && _ref7 !== void 0 ? _ref7 : null, - acres: parseFloat((_ref9 = (_ref0 = (_item$acres = item.acres) !== null && _item$acres !== void 0 ? _item$acres : item.area) !== null && _ref0 !== void 0 ? _ref0 : item.size) !== null && _ref9 !== void 0 ? _ref9 : item.acreage) || null, - soilType: (_ref1 = (_ref10 = (_item$soilType = item.soilType) !== null && _item$soilType !== void 0 ? _item$soilType : item.soil_type) !== null && _ref10 !== void 0 ? _ref10 : item.soil) !== null && _ref1 !== void 0 ? _ref1 : null, - coordinates: { - lat: parseFloat((_ref11 = (_item$lat = item.lat) !== null && _item$lat !== void 0 ? _item$lat : item.latitude) !== null && _ref11 !== void 0 ? _ref11 : (_item$coordinates = item.coordinates) === null || _item$coordinates === void 0 ? void 0 : _item$coordinates.lat) || null, - lon: parseFloat((_ref12 = (_ref13 = (_ref14 = (_item$lon = item.lon) !== null && _item$lon !== void 0 ? _item$lon : item.lng) !== null && _ref14 !== void 0 ? _ref14 : item.longitude) !== null && _ref13 !== void 0 ? _ref13 : (_item$coordinates2 = item.coordinates) === null || _item$coordinates2 === void 0 ? void 0 : _item$coordinates2.lon) !== null && _ref12 !== void 0 ? _ref12 : (_item$coordinates3 = item.coordinates) === null || _item$coordinates3 === void 0 ? void 0 : _item$coordinates3.lng) || null - }, - notes: (_ref15 = (_ref16 = (_item$notes = item.notes) !== null && _item$notes !== void 0 ? _item$notes : item.description) !== null && _ref16 !== void 0 ? _ref16 : item.comments) !== null && _ref15 !== void 0 ? _ref15 : null, - cropHistory: Array.isArray((_item$cropHistory = item.cropHistory) !== null && _item$cropHistory !== void 0 ? _item$cropHistory : item.crop_history) ? (_item$cropHistory2 = item.cropHistory) !== null && _item$cropHistory2 !== void 0 ? _item$cropHistory2 : item.crop_history : [], - harvestRecords: Array.isArray((_item$harvests = item.harvests) !== null && _item$harvests !== void 0 ? _item$harvests : item.harvestRecords) ? (_item$harvests2 = item.harvests) !== null && _item$harvests2 !== void 0 ? _item$harvests2 : item.harvestRecords : [], - carbonPotential: (_item$carbonPotential = item.carbonPotential) !== null && _item$carbonPotential !== void 0 ? _item$carbonPotential : null, - weatherData: null, - createdAt: (_ref17 = (_item$createdAt = item.createdAt) !== null && _item$createdAt !== void 0 ? _item$createdAt : item.created_at) !== null && _ref17 !== void 0 ? _ref17 : new Date().toISOString(), - _source: 'ag-refine' - }); - } - } catch (err) { - _iterator.e(err); - } finally { - _iterator.f(); - } - } - return candidates; -} - -/** - * Loads also come over — map to ingested file records for the dashboard. - */ -function extractLoads(raw) { - var all = _objectSpread(_objectSpread({}, raw.localStorage), raw.sessionStorage); - var loads = []; - for (var _i3 = 0, _Object$entries2 = Object.entries(all); _i3 < _Object$entries2.length; _i3++) { - var _Object$entries2$_i = _slicedToArray(_Object$entries2[_i3], 2), - key = _Object$entries2$_i[0], - val = _Object$entries2$_i[1]; - var k = key.toLowerCase(); - if (!k.includes('load') && !k.includes('scale') && !k.includes('ticket') && !k.includes('delivery')) continue; - var arr = Array.isArray(val) ? val : null; - if (!arr) continue; - var _iterator2 = _createForOfIteratorHelper(arr), - _step2; - try { - for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) { - var item = _step2.value; - if (!item || _typeof(item) !== 'object') continue; - loads.push(item); - } - } catch (err) { - _iterator2.e(err); - } finally { - _iterator2.f(); - } - } - return loads; -} - -// Injected into AG-Refine tab to write field data back into its localStorage -function writeFieldsToAgRefineTab(fields) { - try { - localStorage.setItem('agrifine_pushed_fields', JSON.stringify(fields)); - localStorage.setItem('agrifine_pushed_at', new Date().toISOString()); - // Dispatch an event so a listening AG-Refine app can react immediately - window.dispatchEvent(new CustomEvent('agrifine:fields-updated', { - detail: { - fields: fields - } - })); - return { - ok: true, - count: fields.length - }; - } catch (err) { - return { - ok: false, - error: err.message - }; - } -} -function pushToAgRefine(_x2) { - return _pushToAgRefine.apply(this, arguments); -} -function _pushToAgRefine() { - _pushToAgRefine = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee4(profiles) { - var configuredUrl, allTabs, agRefineTabs, tab, _yield$chrome$scripti, _yield$chrome$scripti2, result, _t7; +function findAgRefineTab(_x2) { + return _findAgRefineTab.apply(this, arguments); +} // ── Functions injected into the AG-Refine tab ───────────────────────────────── +// These run in the page's origin context — same-origin, session cookies included. +// They must be fully self-contained (no closures over outer-scope variables). +function _findAgRefineTab() { + _findAgRefineTab = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee4(configuredUrl) { + var _allTabs$find; + var allTabs; return _regenerator().w(function (_context4) { - while (1) switch (_context4.p = _context4.n) { + while (1) switch (_context4.n) { case 0: _context4.n = 1; - return getAgRefineUrl(); - case 1: - configuredUrl = _context4.v; - _context4.n = 2; return chrome.tabs.query({}); - case 2: + case 1: allTabs = _context4.v; - agRefineTabs = allTabs.filter(function (t) { + return _context4.a(2, (_allTabs$find = allTabs.find(function (t) { return tabMatchesAgRefine(t, configuredUrl); + })) !== null && _allTabs$find !== void 0 ? _allTabs$find : null); + } + }, _callee4); + })); + return _findAgRefineTab.apply(this, arguments); +} +function _injectFetchAll() { + return _injectFetchAll2.apply(this, arguments); +} +function _injectFetchAll2() { + _injectFetchAll2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee6() { + var base, get, _get, _yield$Promise$all, _yield$Promise$all2, fields, tickets, labSamples, harvestPlans; + return _regenerator().w(function (_context6) { + while (1) switch (_context6.n) { + case 0: + _get = function _get3() { + _get = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee5(path) { + var res, _t7; + return _regenerator().w(function (_context5) { + while (1) switch (_context5.p = _context5.n) { + case 0: + _context5.p = 0; + _context5.n = 1; + return fetch(base + path, { + credentials: 'include' + }); + case 1: + res = _context5.v; + if (!(res.status === 401)) { + _context5.n = 2; + break; + } + return _context5.a(2, { + __auth_error: true + }); + case 2: + return _context5.a(2, res.ok ? res.json() : null); + case 3: + _context5.p = 3; + _t7 = _context5.v; + return _context5.a(2, null); + } + }, _callee5, null, [[0, 3]]); + })); + return _get.apply(this, arguments); + }; + get = function _get2(_x5) { + return _get.apply(this, arguments); + }; + base = window.location.origin; + _context6.n = 1; + return Promise.all([get('/api/fields/'), get('/api/scales/tickets/all'), get('/api/intelligence/lab-samples'), get('/api/harvest/plans')]); + case 1: + _yield$Promise$all = _context6.v; + _yield$Promise$all2 = _slicedToArray(_yield$Promise$all, 4); + fields = _yield$Promise$all2[0]; + tickets = _yield$Promise$all2[1]; + labSamples = _yield$Promise$all2[2]; + harvestPlans = _yield$Promise$all2[3]; + return _context6.a(2, { + fields: fields, + tickets: tickets, + labSamples: labSamples, + harvestPlans: harvestPlans }); - if (!(agRefineTabs.length === 0)) { - _context4.n = 3; + } + }, _callee6); + })); + return _injectFetchAll2.apply(this, arguments); +} +function _injectPushFields(_x3) { + return _injectPushFields2.apply(this, arguments); +} // ── Data mapping ────────────────────────────────────────────────────────────── +function _injectPushFields2() { + _injectPushFields2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee7(newFields) { + var base, results, _iterator, _step, field, _body$id, res, body, _t8, _t9, _t0; + return _regenerator().w(function (_context7) { + while (1) switch (_context7.p = _context7.n) { + case 0: + base = window.location.origin; + results = []; + _iterator = _createForOfIteratorHelper(newFields); + _context7.p = 1; + _iterator.s(); + case 2: + if ((_step = _iterator.n()).done) { + _context7.n = 10; break; } - return _context4.a(2, { - ok: false, - error: 'No AG-Refine tab found. Open AG-Refine first.' - }); - case 3: - tab = agRefineTabs[0]; - _context4.p = 4; - _context4.n = 5; - return chrome.scripting.executeScript({ - target: { - tabId: tab.id + field = _step.value; + _context7.p = 3; + _context7.n = 4; + return fetch(base + '/api/fields/', { + method: 'POST', + credentials: 'include', + headers: { + 'Content-Type': 'application/json' }, - func: writeFieldsToAgRefineTab, - args: [profiles] + body: JSON.stringify(field) }); + case 4: + res = _context7.v; + if (!res.ok) { + _context7.n = 6; + break; + } + _context7.n = 5; + return res.json(); case 5: - _yield$chrome$scripti = _context4.v; - _yield$chrome$scripti2 = _slicedToArray(_yield$chrome$scripti, 1); - result = _yield$chrome$scripti2[0]; - return _context4.a(2, result.result); + _t8 = _context7.v; + _context7.n = 7; + break; case 6: - _context4.p = 6; - _t7 = _context4.v; - return _context4.a(2, { + _t8 = null; + case 7: + body = _t8; + results.push({ + name: field.name, + ok: res.ok, + status: res.status, + agRefineId: (_body$id = body === null || body === void 0 ? void 0 : body.id) !== null && _body$id !== void 0 ? _body$id : null + }); + _context7.n = 9; + break; + case 8: + _context7.p = 8; + _t9 = _context7.v; + results.push({ + name: field.name, ok: false, - error: "Cannot write to AG-Refine tab: ".concat(_t7.message) + status: 0, + error: _t9.message }); + case 9: + _context7.n = 2; + break; + case 10: + _context7.n = 12; + break; + case 11: + _context7.p = 11; + _t0 = _context7.v; + _iterator.e(_t0); + case 12: + _context7.p = 12; + _iterator.f(); + return _context7.f(12); + case 13: + return _context7.a(2, results); } - }, _callee4, null, [[4, 6]]); + }, _callee7, null, [[3, 8], [1, 11, 12, 13]]); })); - return _pushToAgRefine.apply(this, arguments); + return _injectPushFields2.apply(this, arguments); +} +function mapAgFieldToProfile(agField, ticketsByFieldId, labSamplesByFieldId) { + var _ticketsByFieldId$agF, _labSamplesByFieldId$, _agField$farm_name, _ref, _agField$acres_fsa, _agField$notes; + var fieldTickets = ((_ticketsByFieldId$agF = ticketsByFieldId[agField.id]) !== null && _ticketsByFieldId$agF !== void 0 ? _ticketsByFieldId$agF : []).filter(function (t) { + return t.net_weight != null; + }).map(function (t) { + var _t$unit, _t$dry_matter_pct, _t$protein_pct, _t$starch_pct, _t$commodity, _t$harvest; + return { + date: t.captured_at, + "yield": t.net_weight, + unit: (_t$unit = t.unit) !== null && _t$unit !== void 0 ? _t$unit : 'lb', + moisture: t.dry_matter_pct != null ? +(100 - t.dry_matter_pct).toFixed(1) : null, + quality: { + dm_pct: (_t$dry_matter_pct = t.dry_matter_pct) !== null && _t$dry_matter_pct !== void 0 ? _t$dry_matter_pct : null, + protein_pct: (_t$protein_pct = t.protein_pct) !== null && _t$protein_pct !== void 0 ? _t$protein_pct : null, + starch_pct: (_t$starch_pct = t.starch_pct) !== null && _t$starch_pct !== void 0 ? _t$starch_pct : null + }, + commodity: (_t$commodity = t.commodity) !== null && _t$commodity !== void 0 ? _t$commodity : null, + harvest_label: (_t$harvest = t.harvest) !== null && _t$harvest !== void 0 ? _t$harvest : null, + ticket_id: t.id, + source: 'ag-refine' + }; + }); + var cropHistory = []; + if (agField.crop) { + var _agField$variety; + var year = agField.planting_date ? parseInt(agField.planting_date.slice(0, 4), 10) : new Date().getFullYear(); + cropHistory.push({ + year: year, + crop: agField.crop, + variety: (_agField$variety = agField.variety) !== null && _agField$variety !== void 0 ? _agField$variety : null + }); + } + var labSamples = ((_labSamplesByFieldId$ = labSamplesByFieldId[agField.id]) !== null && _labSamplesByFieldId$ !== void 0 ? _labSamplesByFieldId$ : []).map(function (s) { + var _s$sampled_at, _s$dry_matter_pct, _s$ndf_pct, _s$adf_pct, _s$rfv, _s$rfq, _s$nel, _s$digestibility_pct; + return { + date: (_s$sampled_at = s.sampled_at) !== null && _s$sampled_at !== void 0 ? _s$sampled_at : null, + dm_pct: (_s$dry_matter_pct = s.dry_matter_pct) !== null && _s$dry_matter_pct !== void 0 ? _s$dry_matter_pct : null, + ndf_pct: (_s$ndf_pct = s.ndf_pct) !== null && _s$ndf_pct !== void 0 ? _s$ndf_pct : null, + adf_pct: (_s$adf_pct = s.adf_pct) !== null && _s$adf_pct !== void 0 ? _s$adf_pct : null, + rfv: (_s$rfv = s.rfv) !== null && _s$rfv !== void 0 ? _s$rfv : null, + rfq: (_s$rfq = s.rfq) !== null && _s$rfq !== void 0 ? _s$rfq : null, + nel: (_s$nel = s.nel) !== null && _s$nel !== void 0 ? _s$nel : null, + digestibility_pct: (_s$digestibility_pct = s.digestibility_pct) !== null && _s$digestibility_pct !== void 0 ? _s$digestibility_pct : null + }; + }); + return { + id: "agr_".concat(agField.id), + name: agField.name, + farmName: (_agField$farm_name = agField.farm_name) !== null && _agField$farm_name !== void 0 ? _agField$farm_name : null, + acres: (_ref = (_agField$acres_fsa = agField.acres_fsa) !== null && _agField$acres_fsa !== void 0 ? _agField$acres_fsa : agField.acres_calculated) !== null && _ref !== void 0 ? _ref : null, + soilType: null, + coordinates: { + lat: null, + lon: null + }, + notes: (_agField$notes = agField.notes) !== null && _agField$notes !== void 0 ? _agField$notes : null, + cropHistory: cropHistory, + harvestRecords: fieldTickets, + labSamples: labSamples, + carbonPotential: null, + weatherData: null, + createdAt: new Date().toISOString(), + _source: 'ag-refine', + _agRefineId: agField.id + }; } + +// ── Public API ──────────────────────────────────────────────────────────────── + function syncFromAgRefine() { return _syncFromAgRefine.apply(this, arguments); } function _syncFromAgRefine() { - _syncFromAgRefine = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee5() { - var configuredUrl, allTabs, agRefineTabs, tab, raw, _yield$chrome$scripti3, _yield$chrome$scripti4, result, fields, loads, existing, added, updated, _iterator3, _step3, _loop, log, history, _t8, _t9; - return _regenerator().w(function (_context6) { - while (1) switch (_context6.p = _context6.n) { + _syncFromAgRefine = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee8() { + var _raw$fields; + var configuredUrl, tab, raw, _yield$chrome$scripti, _yield$chrome$scripti2, result, agFields, allTickets, allLabSamples, ticketsByFieldId, _iterator2, _step2, _t$field_id, _ticketsByFieldId$_t$, t, labSamplesByFieldId, _iterator3, _step3, _s$field_id, _labSamplesByFieldId$2, s, incomingProfiles, existing, added, updated, _iterator4, _step4, _loop, logEntry, history, _t1, _t10, _t11, _t12; + return _regenerator().w(function (_context9) { + while (1) switch (_context9.p = _context9.n) { case 0: - _context6.n = 1; + _context9.n = 1; return getAgRefineUrl(); case 1: - configuredUrl = _context6.v; - _context6.n = 2; - return chrome.tabs.query({}); + configuredUrl = _context9.v; + _context9.n = 2; + return findAgRefineTab(configuredUrl); case 2: - allTabs = _context6.v; - agRefineTabs = allTabs.filter(function (t) { - return tabMatchesAgRefine(t, configuredUrl); - }); - if (!(agRefineTabs.length === 0)) { - _context6.n = 3; + tab = _context9.v; + if (tab) { + _context9.n = 3; break; } - return _context6.a(2, { + return _context9.a(2, { ok: false, error: 'No AG-Refine tab found. Open AG-Refine in a browser tab first.' }); case 3: - tab = agRefineTabs[0]; - _context6.p = 4; - _context6.n = 5; + _context9.p = 3; + _context9.n = 4; return chrome.scripting.executeScript({ target: { tabId: tab.id }, - func: scrapeAgRefineTab + func: _injectFetchAll }); - case 5: - _yield$chrome$scripti3 = _context6.v; - _yield$chrome$scripti4 = _slicedToArray(_yield$chrome$scripti3, 1); - result = _yield$chrome$scripti4[0]; + case 4: + _yield$chrome$scripti = _context9.v; + _yield$chrome$scripti2 = _slicedToArray(_yield$chrome$scripti, 1); + result = _yield$chrome$scripti2[0]; raw = result.result; - _context6.n = 7; + _context9.n = 6; break; + case 5: + _context9.p = 5; + _t1 = _context9.v; + return _context9.a(2, { + ok: false, + error: "Cannot reach AG-Refine tab: ".concat(_t1.message) + }); case 6: - _context6.p = 6; - _t8 = _context6.v; - return _context6.a(2, { + if (!(!raw || (_raw$fields = raw.fields) !== null && _raw$fields !== void 0 && _raw$fields.__auth_error)) { + _context9.n = 7; + break; + } + return _context9.a(2, { ok: false, - error: "Cannot read AG-Refine tab: ".concat(_t8.message) + error: 'AG-Refine returned 401 — please log in to AG-Refine first.' }); case 7: - fields = extractFields(raw); - loads = extractLoads(raw); // Merge fields — update existing by name, insert new ones - _context6.n = 8; + agFields = Array.isArray(raw.fields) ? raw.fields.filter(function (f) { + return f.active !== false; + }) : []; + allTickets = Array.isArray(raw.tickets) ? raw.tickets : []; + allLabSamples = Array.isArray(raw.labSamples) ? raw.labSamples : []; // Index by field_id for O(1) lookup + ticketsByFieldId = {}; + _iterator2 = _createForOfIteratorHelper(allTickets); + _context9.p = 8; + _iterator2.s(); + case 9: + if ((_step2 = _iterator2.n()).done) { + _context9.n = 12; + break; + } + t = _step2.value; + if (!(t.field_id == null)) { + _context9.n = 10; + break; + } + return _context9.a(3, 11); + case 10: + ((_ticketsByFieldId$_t$ = ticketsByFieldId[_t$field_id = t.field_id]) !== null && _ticketsByFieldId$_t$ !== void 0 ? _ticketsByFieldId$_t$ : ticketsByFieldId[_t$field_id] = []).push(t); + case 11: + _context9.n = 9; + break; + case 12: + _context9.n = 14; + break; + case 13: + _context9.p = 13; + _t10 = _context9.v; + _iterator2.e(_t10); + case 14: + _context9.p = 14; + _iterator2.f(); + return _context9.f(14); + case 15: + labSamplesByFieldId = {}; + _iterator3 = _createForOfIteratorHelper(allLabSamples); + _context9.p = 16; + _iterator3.s(); + case 17: + if ((_step3 = _iterator3.n()).done) { + _context9.n = 20; + break; + } + s = _step3.value; + if (!(s.field_id == null)) { + _context9.n = 18; + break; + } + return _context9.a(3, 19); + case 18: + ((_labSamplesByFieldId$2 = labSamplesByFieldId[_s$field_id = s.field_id]) !== null && _labSamplesByFieldId$2 !== void 0 ? _labSamplesByFieldId$2 : labSamplesByFieldId[_s$field_id] = []).push(s); + case 19: + _context9.n = 17; + break; + case 20: + _context9.n = 22; + break; + case 21: + _context9.p = 21; + _t11 = _context9.v; + _iterator3.e(_t11); + case 22: + _context9.p = 22; + _iterator3.f(); + return _context9.f(22); + case 23: + incomingProfiles = agFields.map(function (f) { + return mapAgFieldToProfile(f, ticketsByFieldId, labSamplesByFieldId); + }); + _context9.n = 24; return (0,_storage_js__WEBPACK_IMPORTED_MODULE_0__.getFieldProfiles)(); - case 8: - existing = _context6.v; + case 24: + existing = _context9.v; added = 0; updated = 0; - _iterator3 = _createForOfIteratorHelper(fields); - _context6.p = 9; + _iterator4 = _createForOfIteratorHelper(incomingProfiles); + _context9.p = 25; _loop = /*#__PURE__*/_regenerator().m(function _loop() { - var f, match, _match$coordinates, _match$cropHistory, _match$notes, _match$cluId, merged; - return _regenerator().w(function (_context5) { - while (1) switch (_context5.n) { + var _existing$find; + var profile, match, _match$cropHistory, _match$harvestRecords, _match$soilType, _match$coordinates, _match$notes; + return _regenerator().w(function (_context8) { + while (1) switch (_context8.n) { case 0: - f = _step3.value; - match = existing.find(function (e) { - return e.name.toLowerCase() === f.name.toLowerCase(); + profile = _step4.value; + match = (_existing$find = existing.find(function (e) { + return e._agRefineId === profile._agRefineId; + })) !== null && _existing$find !== void 0 ? _existing$find : existing.find(function (e) { + return e.name.toLowerCase() === profile.name.toLowerCase(); }); if (!match) { - _context5.n = 2; + _context8.n = 2; break; } - // Merge: fill in missing data without overwriting user edits - merged = _objectSpread(_objectSpread(_objectSpread({}, f), match), {}, { - coordinates: ((_match$coordinates = match.coordinates) === null || _match$coordinates === void 0 ? void 0 : _match$coordinates.lat) != null ? match.coordinates : f.coordinates, - cropHistory: (_match$cropHistory = match.cropHistory) !== null && _match$cropHistory !== void 0 && _match$cropHistory.length ? match.cropHistory : f.cropHistory, - notes: (_match$notes = match.notes) !== null && _match$notes !== void 0 ? _match$notes : f.notes, - cluId: (_match$cluId = match.cluId) !== null && _match$cluId !== void 0 ? _match$cluId : f.cluId, + _context8.n = 1; + return (0,_storage_js__WEBPACK_IMPORTED_MODULE_0__.saveFieldProfile)(_objectSpread(_objectSpread({}, profile), {}, { + id: match.id, + cropHistory: (_match$cropHistory = match.cropHistory) !== null && _match$cropHistory !== void 0 && _match$cropHistory.length ? match.cropHistory : profile.cropHistory, + harvestRecords: profile.harvestRecords.length ? profile.harvestRecords : (_match$harvestRecords = match.harvestRecords) !== null && _match$harvestRecords !== void 0 ? _match$harvestRecords : [], + soilType: (_match$soilType = match.soilType) !== null && _match$soilType !== void 0 ? _match$soilType : profile.soilType, + coordinates: ((_match$coordinates = match.coordinates) === null || _match$coordinates === void 0 ? void 0 : _match$coordinates.lat) != null ? match.coordinates : profile.coordinates, + notes: (_match$notes = match.notes) !== null && _match$notes !== void 0 ? _match$notes : profile.notes, _source: 'ag-refine-merged' - }); - _context5.n = 1; - return (0,_storage_js__WEBPACK_IMPORTED_MODULE_0__.saveFieldProfile)(merged); + })); case 1: updated++; - _context5.n = 4; + _context8.n = 4; break; case 2: - _context5.n = 3; - return (0,_storage_js__WEBPACK_IMPORTED_MODULE_0__.saveFieldProfile)(f); + _context8.n = 3; + return (0,_storage_js__WEBPACK_IMPORTED_MODULE_0__.saveFieldProfile)(profile); case 3: added++; case 4: - return _context5.a(2); + return _context8.a(2); } }, _loop); }); - _iterator3.s(); - case 10: - if ((_step3 = _iterator3.n()).done) { - _context6.n = 12; + _iterator4.s(); + case 26: + if ((_step4 = _iterator4.n()).done) { + _context9.n = 28; break; } - return _context6.d(_regeneratorValues(_loop()), 11); - case 11: - _context6.n = 10; + return _context9.d(_regeneratorValues(_loop()), 27); + case 27: + _context9.n = 26; break; - case 12: - _context6.n = 14; + case 28: + _context9.n = 30; break; - case 13: - _context6.p = 13; - _t9 = _context6.v; - _iterator3.e(_t9); - case 14: - _context6.p = 14; - _iterator3.f(); - return _context6.f(14); - case 15: - log = { + case 29: + _context9.p = 29; + _t12 = _context9.v; + _iterator4.e(_t12); + case 30: + _context9.p = 30; + _iterator4.f(); + return _context9.f(30); + case 31: + logEntry = { at: new Date().toISOString(), tabUrl: tab.url, fieldsAdded: added, fieldsUpdated: updated, - loadsFound: loads.length, - rawKeys: Object.keys(_objectSpread(_objectSpread({}, raw.localStorage), raw.sessionStorage)) + ticketsPulled: allTickets.length, + labSamplesPulled: allLabSamples.length }; - _context6.n = 16; + _context9.n = 32; return getSyncLog(); - case 16: - history = _context6.v; - history.unshift(log); - _context6.n = 17; + case 32: + history = _context9.v; + history.unshift(logEntry); + _context9.n = 33; return (0,_storage_js__WEBPACK_IMPORTED_MODULE_0__.localSet)(SYNC_LOG_KEY, history.slice(0, 20)); - case 17: - return _context6.a(2, { + case 33: + return _context9.a(2, { ok: true, added: added, updated: updated, - loadsFound: loads.length, - loads: loads, - tabUrl: tab.url + ticketsPulled: allTickets.length, + labSamplesPulled: allLabSamples.length }); } - }, _callee5, null, [[9, 13, 14, 15], [4, 6]]); + }, _callee8, null, [[25, 29, 30, 31], [16, 21, 22, 23], [8, 13, 14, 15], [3, 5]]); })); return _syncFromAgRefine.apply(this, arguments); } +function pushToAgRefine(_x4) { + return _pushToAgRefine.apply(this, arguments); +} +function _pushToAgRefine() { + _pushToAgRefine = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee9(profiles) { + var configuredUrl, tab, toCreate, _result$result, _yield$chrome$scripti3, _yield$chrome$scripti4, result, results, pushed, failures, existing, _iterator5, _step5, _loop2, _t13, _t14; + return _regenerator().w(function (_context1) { + while (1) switch (_context1.p = _context1.n) { + case 0: + _context1.n = 1; + return getAgRefineUrl(); + case 1: + configuredUrl = _context1.v; + _context1.n = 2; + return findAgRefineTab(configuredUrl); + case 2: + tab = _context1.v; + if (tab) { + _context1.n = 3; + break; + } + return _context1.a(2, { + ok: false, + error: 'No AG-Refine tab found. Open AG-Refine in a browser tab first.' + }); + case 3: + // Only push profiles that haven't been synced from AG-Refine yet + toCreate = profiles.filter(function (p) { + return !p._agRefineId; + }).map(function (p) { + var _p$farmName, _p$acres, _p$cropHistory$0$crop, _p$cropHistory, _p$cropHistory$0$vari, _p$cropHistory2, _p$notes; + return { + name: p.name, + farm_name: (_p$farmName = p.farmName) !== null && _p$farmName !== void 0 ? _p$farmName : null, + acres_fsa: (_p$acres = p.acres) !== null && _p$acres !== void 0 ? _p$acres : null, + crop: (_p$cropHistory$0$crop = (_p$cropHistory = p.cropHistory) === null || _p$cropHistory === void 0 || (_p$cropHistory = _p$cropHistory[0]) === null || _p$cropHistory === void 0 ? void 0 : _p$cropHistory.crop) !== null && _p$cropHistory$0$crop !== void 0 ? _p$cropHistory$0$crop : null, + variety: (_p$cropHistory$0$vari = (_p$cropHistory2 = p.cropHistory) === null || _p$cropHistory2 === void 0 || (_p$cropHistory2 = _p$cropHistory2[0]) === null || _p$cropHistory2 === void 0 ? void 0 : _p$cropHistory2.variety) !== null && _p$cropHistory$0$vari !== void 0 ? _p$cropHistory$0$vari : null, + notes: (_p$notes = p.notes) !== null && _p$notes !== void 0 ? _p$notes : null + }; + }); + if (!(toCreate.length === 0)) { + _context1.n = 4; + break; + } + return _context1.a(2, { + ok: true, + pushed: 0, + message: 'All profiles are already synced with AG-Refine.' + }); + case 4: + _context1.p = 4; + _context1.n = 5; + return chrome.scripting.executeScript({ + target: { + tabId: tab.id + }, + func: _injectPushFields, + args: [toCreate] + }); + case 5: + _yield$chrome$scripti3 = _context1.v; + _yield$chrome$scripti4 = _slicedToArray(_yield$chrome$scripti3, 1); + result = _yield$chrome$scripti4[0]; + results = (_result$result = result.result) !== null && _result$result !== void 0 ? _result$result : []; + pushed = results.filter(function (r) { + return r.ok; + }).length; + failures = results.filter(function (r) { + return !r.ok; + }); // Back-fill _agRefineId on successfully pushed profiles + _context1.n = 6; + return (0,_storage_js__WEBPACK_IMPORTED_MODULE_0__.getFieldProfiles)(); + case 6: + existing = _context1.v; + _iterator5 = _createForOfIteratorHelper(results); + _context1.p = 7; + _loop2 = /*#__PURE__*/_regenerator().m(function _loop2() { + var res, p; + return _regenerator().w(function (_context0) { + while (1) switch (_context0.n) { + case 0: + res = _step5.value; + if (!(!res.ok || !res.agRefineId)) { + _context0.n = 1; + break; + } + return _context0.a(2, 1); + case 1: + p = existing.find(function (e) { + return e.name === res.name; + }); + if (!p) { + _context0.n = 2; + break; + } + _context0.n = 2; + return (0,_storage_js__WEBPACK_IMPORTED_MODULE_0__.saveFieldProfile)(_objectSpread(_objectSpread({}, p), {}, { + _agRefineId: res.agRefineId, + _source: 'ag-refine-merged' + })); + case 2: + return _context0.a(2); + } + }, _loop2); + }); + _iterator5.s(); + case 8: + if ((_step5 = _iterator5.n()).done) { + _context1.n = 11; + break; + } + return _context1.d(_regeneratorValues(_loop2()), 9); + case 9: + if (!_context1.v) { + _context1.n = 10; + break; + } + return _context1.a(3, 10); + case 10: + _context1.n = 8; + break; + case 11: + _context1.n = 13; + break; + case 12: + _context1.p = 12; + _t13 = _context1.v; + _iterator5.e(_t13); + case 13: + _context1.p = 13; + _iterator5.f(); + return _context1.f(13); + case 14: + return _context1.a(2, { + ok: failures.length === 0, + pushed: pushed, + failures: failures.length ? failures : undefined + }); + case 15: + _context1.p = 15; + _t14 = _context1.v; + return _context1.a(2, { + ok: false, + error: "Cannot write to AG-Refine: ".concat(_t14.message) + }); + } + }, _callee9, null, [[7, 12, 13, 14], [4, 15]]); + })); + return _pushToAgRefine.apply(this, arguments); +} /***/ }, @@ -1245,28 +1519,39 @@ function _buildContextBundle() { // ── 2. Field profiles with crop history and harvest records ────────────────── fieldLines = profiles.length === 0 ? ['(none)'] : profiles.map(function (p) { - var _p$coordinates, _p$coordinates2, _p$cropHistory, _p$harvestRecords, _p$acres, _p$soilType; + var _p$coordinates, _p$coordinates2, _p$cropHistory, _p$harvestRecords, _slice$map$, _p$labSamples, _p$acres, _p$soilType; var coords = ((_p$coordinates = p.coordinates) === null || _p$coordinates === void 0 ? void 0 : _p$coordinates.lat) != null && ((_p$coordinates2 = p.coordinates) === null || _p$coordinates2 === void 0 ? void 0 : _p$coordinates2.lon) != null ? "".concat(p.coordinates.lat.toFixed(4), ", ").concat(p.coordinates.lon.toFixed(4)) : null; var history = ((_p$cropHistory = p.cropHistory) !== null && _p$cropHistory !== void 0 ? _p$cropHistory : []).slice(0, 4).map(function (h) { return "".concat(h.year, ": ").concat(h.crop); }).join(', '); var harvests = ((_p$harvestRecords = p.harvestRecords) !== null && _p$harvestRecords !== void 0 ? _p$harvestRecords : []).slice(0, 3).map(function (h) { - var _h$date$slice, _h$date, _h$unit; - return "".concat((_h$date$slice = (_h$date = h.date) === null || _h$date === void 0 ? void 0 : _h$date.slice(0, 10)) !== null && _h$date$slice !== void 0 ? _h$date$slice : '?', ": ").concat(h["yield"], " ").concat((_h$unit = h.unit) !== null && _h$unit !== void 0 ? _h$unit : '').trim(); + var _ref, _h$commodity, _h$unit, _h$quality, _h$date$slice, _h$date; + var label = (_ref = (_h$commodity = h.commodity) !== null && _h$commodity !== void 0 ? _h$commodity : h.crop) !== null && _ref !== void 0 ? _ref : 'load'; + var qty = h["yield"] != null ? "".concat(Number(h["yield"]).toLocaleString(), " ").concat((_h$unit = h.unit) !== null && _h$unit !== void 0 ? _h$unit : 'lb') : '?'; + var dm = ((_h$quality = h.quality) === null || _h$quality === void 0 ? void 0 : _h$quality.dm_pct) != null ? " DM".concat(h.quality.dm_pct, "%") : ''; + return "".concat((_h$date$slice = (_h$date = h.date) === null || _h$date === void 0 ? void 0 : _h$date.slice(0, 10)) !== null && _h$date$slice !== void 0 ? _h$date$slice : '?', " ").concat(label, ": ").concat(qty).concat(dm); }).join('; '); - var parts = ["Field \"".concat(p.name, "\" | ").concat((_p$acres = p.acres) !== null && _p$acres !== void 0 ? _p$acres : '?', " ac | ").concat((_p$soilType = p.soilType) !== null && _p$soilType !== void 0 ? _p$soilType : 'unknown soil'), coords ? " Coords: ".concat(coords) : null, p.cluId ? " CLU: ".concat(p.cluId) : null, history ? " Crop history: ".concat(history) : null, harvests ? " Harvests: ".concat(harvests) : null, p.notes ? " Notes: ".concat(p.notes) : null]; + var latestLab = (_slice$map$ = ((_p$labSamples = p.labSamples) !== null && _p$labSamples !== void 0 ? _p$labSamples : []).slice(0, 1).map(function (s) { + var parts = []; + if (s.dm_pct != null) parts.push("DM ".concat(s.dm_pct, "%")); + if (s.ndf_pct != null) parts.push("NDF ".concat(s.ndf_pct, "%")); + if (s.rfv != null) parts.push("RFV ".concat(s.rfv)); + if (s.nel != null) parts.push("NEL ".concat(s.nel)); + return parts.join(', '); + })[0]) !== null && _slice$map$ !== void 0 ? _slice$map$ : null; + var parts = ["Field \"".concat(p.name, "\" | ").concat((_p$acres = p.acres) !== null && _p$acres !== void 0 ? _p$acres : '?', " ac | ").concat((_p$soilType = p.soilType) !== null && _p$soilType !== void 0 ? _p$soilType : 'unknown soil'), coords ? " Coords: ".concat(coords) : null, p.cluId ? " CLU: ".concat(p.cluId) : null, history ? " Crop history: ".concat(history) : null, harvests ? " Harvests: ".concat(harvests) : null, latestLab ? " Latest lab: ".concat(latestLab) : null, p.notes ? " Notes: ".concat(p.notes) : null]; return parts.filter(Boolean).join('\n'); }); // ── 3. Ingested data files ─────────────────────────────────────────────────── fileLines = files.length === 0 ? ['(none)'] : files.slice(0, 10).map(function (f) { var _f$preview$slice, _f$preview, _f$uploadedAt$slice, _f$uploadedAt; - var preview = f.structuredData ? Object.entries(f.structuredData).filter(function (_ref) { - var _ref2 = _slicedToArray(_ref, 1), - k = _ref2[0]; + var preview = f.structuredData ? Object.entries(f.structuredData).filter(function (_ref2) { + var _ref3 = _slicedToArray(_ref2, 1), + k = _ref3[0]; return k !== 'raw_preview' && k !== 'parse_error'; - }).slice(0, 5).map(function (_ref3) { - var _ref4 = _slicedToArray(_ref3, 2), - k = _ref4[0], - v = _ref4[1]; + }).slice(0, 5).map(function (_ref4) { + var _ref5 = _slicedToArray(_ref4, 2), + k = _ref5[0], + v = _ref5[1]; return "".concat(k, ": ").concat(JSON.stringify(v).slice(0, 120)); }).join(' | ') : (_f$preview$slice = (_f$preview = f.preview) === null || _f$preview === void 0 ? void 0 : _f$preview.slice(0, 200)) !== null && _f$preview$slice !== void 0 ? _f$preview$slice : '(no structured data)'; return "[".concat(f.type, "] ").concat(f.filename, " (").concat((_f$uploadedAt$slice = (_f$uploadedAt = f.uploadedAt) === null || _f$uploadedAt === void 0 ? void 0 : _f$uploadedAt.slice(0, 10)) !== null && _f$uploadedAt$slice !== void 0 ? _f$uploadedAt$slice : '?', "): ").concat(preview); diff --git a/agrifine-extension/dist/sidebar.css b/agrifine-extension/dist/sidebar.css index 270786e1f6..7aced9cc78 100644 --- a/agrifine-extension/dist/sidebar.css +++ b/agrifine-extension/dist/sidebar.css @@ -962,6 +962,9 @@ video { .text-center { text-align: center; } +.text-right { + text-align: right; +} .font-sans { font-family: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; } diff --git a/agrifine-extension/dist/sidebar.js b/agrifine-extension/dist/sidebar.js index c17c74db82..145f268963 100644 --- a/agrifine-extension/dist/sidebar.js +++ b/agrifine-extension/dist/sidebar.js @@ -330,7 +330,7 @@ var COMMITTEE = [{ accentColor: '#d97706', borderStyle: 'border-l-[3px]', borderColor: '#d97706', - persona: "You are Kount Kuekkens, a retired agricultural economist now serving as CFO for this farm operation. You speak with a spiraling rhetorical style \u2014 you begin broad, drift into economic theory or historical context, but always land on concrete \"Dairy Moneyball\" math that actually matters.\n\nYour domain: Income Over Feed Cost (IOFC), commodity price impacts on margins, feed efficiency ratios, processor quality premium/penalty thresholds, cash flow position, budget variances, and the financial consequences of operational data errors.\n\nWhen you spot data problems, quantify the financial blindspot they create. When you see opportunities, express them in dollar terms. You are candid about when you are \"spiraling\" into uncertainty vs. when you have hard numbers. You occasionally reference obscure economic principles before getting to the point.\n\nReport in 3\u20134 paragraphs. Be specific \u2014 name dollar figures, percentages, and cite the data points you are drawing from." + persona: "You are Kount Kuekkens, a retired agricultural economist now serving as CFO for this farm operation. You speak with a spiraling rhetorical style \u2014 you begin broad, drift into economic theory or historical context, but always land on concrete \"Dairy Moneyball\" math that actually matters.\n\nYour domain: Income Over Feed Cost (IOFC), commodity price impacts on margins, feed efficiency ratios, processor quality premium/penalty thresholds, cash flow position, budget variances, and the financial consequences of operational data errors.\n\nYou have access to the AG-Refine weigh ticket ledger \u2014 gross/tare/net weights, dry matter percentages per load, commodity labels, and harvest labels (e.g. \"2026 1st Cut Alfalfa\"). You cross-reference these against field operation costs (estimated fuel gallons, cost-per-hour equipment charges) to compute true cost-per-ton delivered. When DM% is missing from tickets, you quantify the exact financial blindspot: a 5-point swing in DM on a 40-ton load at $100/ton DM-adjusted is a $200 uncertainty per truck. You also flag integrity score patterns on field operations \u2014 a cluster of low-integrity scores means acreage data is unreliable, and your acreage-based cost allocations are fiction.\n\nWhen you spot data problems, quantify the financial blindspot they create. When you see opportunities, express them in dollar terms. You are candid about when you are \"spiraling\" into uncertainty vs. when you have hard numbers. You occasionally reference obscure economic principles before getting to the point.\n\nReport in 3\u20134 paragraphs. Be specific \u2014 name dollar figures, percentages, and cite the data points you are drawing from." }, { id: 'crops', name: 'Rolf Forage', @@ -338,7 +338,7 @@ var COMMITTEE = [{ emoji: '🌾', accentColor: '#16a34a', borderColor: '#16a34a', - persona: "You are Rolf Forage (pronounced \"For-ahh-juz\"), a fiercely opinionated agronomist and crops director. You do not care about spreadsheets or financial models \u2014 you care about what is actually in the field and the bunker right now.\n\nYour domain: forage quality (dry matter, NDF, fiber digestibility), silage inventory and fermentation integrity, harvest timing windows, field conditions (soil type, drainage, compaction), cover crop programs, nutrient cycling, and input scheduling.\n\nYou are demanding and direct. If the data shows a crop problem that will compromise feed quality, you say so loudly and insist it be corrected immediately \u2014 you do not sugarcoat risk to protect someone's budget. You will call out the financial team for cutting corners that ultimately cost more in lost production. You speak in practical, field-level language.\n\nReport in 3\u20134 paragraphs. Be opinionated and specific about what needs to happen and when." + persona: "You are Rolf Forage (pronounced \"For-ahh-juz\"), a fiercely opinionated agronomist and crops director. You do not care about spreadsheets or financial models \u2014 you care about what is actually in the field and the bunker right now.\n\nYour domain: forage quality (dry matter, NDF, fiber digestibility), silage inventory and fermentation integrity, harvest timing windows, field conditions (soil type, drainage, compaction), cover crop programs, nutrient cycling, and input scheduling.\n\nYou can read the AG-Refine lab sample ledger: NIR / wet-chem results with DM%, NDF%, ADF%, RFQ, RFV, and NEL values per field. You know the thresholds: alfalfa hay above 40% NDF is past maturity; RFV below 150 is marginal dairy quality; NEL below 0.65 Mcal/lb hurts milk production. You also read harvest operation records \u2014 operation types (mow, merge, chop, haul), acres covered, timing \u2014 and cross-reference against the planned harvest calendar (cut number, planned date, window days) to call out whether a cut happened on schedule or was delayed and why that matters for next cut regrowth. You will call out fields with missing lab samples after harvest as a quality audit failure.\n\nYou are demanding and direct. If the data shows a crop problem that will compromise feed quality, you say so loudly and insist it be corrected immediately \u2014 you do not sugarcoat risk to protect someone's budget. You will call out the financial team for cutting corners that ultimately cost more in lost production. You speak in practical, field-level language.\n\nReport in 3\u20134 paragraphs. Be opinionated and specific about what needs to happen and when." }, { id: 'herd', name: 'Dr. Vera Hest', @@ -346,7 +346,7 @@ var COMMITTEE = [{ emoji: '🐄', accentColor: '#60a5fa', borderColor: '#60a5fa', - persona: "You are Dr. Vera Hest, a sharp-witted, data-driven veterinarian and herd health director. You value biological metrics and animal welfare above all else \u2014 and you will challenge any department that proposes to compromise herd health in the name of cost savings or operational convenience.\n\nYour domain: Somatic Cell Count (SCC) trends and penalty risk, Dry Matter Intake (DMI) per cow, Body Condition Score (BCS), transition cow health, Temperature-Humidity Index (THI) and heat stress protocol, milk component trends (fat, protein), reproductive performance, and disease incidence (ketosis, mastitis, lameness, displaced abomasum).\n\nYou connect biological metrics to production outcomes \u2014 a BCS over 3.75 at calving means dystocia and ketosis next month; a THI of 86 means DMI drops 10\u201315% and milk yield follows within 48 hours. You are precise with thresholds, not vague. You speak clinically but translate findings for the group when needed.\n\nReport in 3\u20134 paragraphs. Be incisive. Cite specific thresholds and explain their downstream consequences." + persona: "You are Dr. Vera Hest, a sharp-witted, data-driven veterinarian and herd health director. You value biological metrics and animal welfare above all else \u2014 and you will challenge any department that proposes to compromise herd health in the name of cost savings or operational convenience.\n\nYour domain: Somatic Cell Count (SCC) trends and penalty risk, Dry Matter Intake (DMI) per cow, Body Condition Score (BCS), transition cow health, Temperature-Humidity Index (THI) and heat stress protocol, milk component trends (fat, protein), reproductive performance, and disease incidence (ketosis, mastitis, lameness, displaced abomasum).\n\nYou connect forage quality data from the AG-Refine lab samples to production outcomes: when NDF exceeds threshold, effective fiber is limiting DMI; when NEL drops, the energy deficit drives BCS loss and ketosis risk in fresh cows. Weigh ticket DM% is also your leading indicator \u2014 a load coming in at 32% DM that should be 35% means the pile is wetter and will ferment differently, affecting palatability and intake 60\u201390 days post-ensiling. You cross-reference forage delivery timing (haul operations) against pen headcount logic: a field generating 400 tons of silage at 35% DM feeds X cow-days \u2014 you can estimate whether inventory covers the next cutting cycle.\n\nYou are precise with thresholds, not vague. You speak clinically but translate findings for the group when needed. You will challenge any budget decision that compromises ration quality or requires delaying a pen check protocol.\n\nReport in 3\u20134 paragraphs. Be incisive. Cite specific thresholds and explain their downstream consequences." }, { id: 'personnel', name: 'Marla Shift', @@ -354,7 +354,7 @@ var COMMITTEE = [{ emoji: '📋', accentColor: '#94a3b8', borderColor: '#94a3b8', - persona: "You are Marla Shift, the operations-hardened manager who oversees labor, personnel scheduling, equipment maintenance, and day-to-day execution. You are the \"reality check\" of the boardroom.\n\nYour domain: labor availability and shift coverage, overtime costs and crew fatigue, equipment uptime and maintenance backlogs, safety compliance, training gaps, and operational root causes of data errors or production misses.\n\nWhen the other advisors make demands \u2014 Rolf needs an early harvest crew, Vera wants manual pen checks every two hours, Kount wants a new validation system built by Friday \u2014 you translate those demands into actual execution requirements: how many people, how many hours, what it costs, and what else will be delayed or skipped to make it happen.\n\nYou provide honest operational explanations (not excuses) for why things went wrong: mechanical failures, staffing gaps during peak periods, training slips under pressure. You are pragmatic, occasionally exasperated, and very good at finding workarounds under real-world constraints.\n\nReport in 3\u20134 paragraphs. Be concrete about labor, time, and resource constraints." + persona: "You are Marla Shift, the operations-hardened manager who oversees labor, personnel scheduling, equipment maintenance, and day-to-day execution. You are the \"reality check\" of the boardroom.\n\nYour domain: labor availability and shift coverage, overtime costs and crew fatigue, equipment uptime and maintenance backlogs, safety compliance, training gaps, and operational root causes of data errors or production misses.\n\nYou read the AG-Refine operations ledger directly: field operations by device, start/end times, duration, acres covered, and status (auto/confirmed/rejected). You also see the driver-truck assignment log and fleet roster. When an operation shows \"auto\" status for days, that tells you nobody is reviewing the data \u2014 a training problem or bandwidth problem. When you see a harvest operation that ran 14 hours continuous on one device, that tells you crew fatigue was a factor. Low integrity scores on a confirmed operation tell you the confirming supervisor didn't actually validate the data. You track cycle time from weigh tickets: if field_entry_time to scale_arrival_time is trending up, transit is bottlenecking throughput and you know which truck assignment is the culprit.\n\nWhen the other advisors make demands \u2014 Rolf needs an early harvest crew, Vera wants manual pen checks every two hours, Kount wants a new validation system built by Friday \u2014 you translate those demands into actual execution requirements: how many people, how many hours, what it costs, and what else will be delayed or skipped to make it happen.\n\nYou provide honest operational explanations (not excuses) for why things went wrong: mechanical failures, staffing gaps during peak periods, training slips under pressure. You are pragmatic, occasionally exasperated, and very good at finding workarounds under real-world constraints.\n\nReport in 3\u20134 paragraphs. Be concrete about labor, time, and resource constraints." }]; /** @@ -3021,7 +3021,8 @@ function FieldProfileModule() { parts = []; if (result.added) parts.push("".concat(result.added, " field").concat(result.added !== 1 ? 's' : '', " added")); if (result.updated) parts.push("".concat(result.updated, " updated")); - if (result.loadsFound) parts.push("".concat(result.loadsFound, " loads found")); + if (result.ticketsPulled) parts.push("".concat(result.ticketsPulled, " tickets")); + if (result.labSamplesPulled) parts.push("".concat(result.labSamplesPulled, " lab samples")); statusEl.textContent = parts.length ? "\u2713 Pull complete \u2014 ".concat(parts.join(', ')) : '✓ No new fields in AG-Refine'; statusEl.style.color = '#4ade80'; setTimeout(function () { @@ -3066,7 +3067,7 @@ function FieldProfileModule() { }, 6000); return _context4.a(2); case 2: - statusEl.textContent = "\u2713 Pushed ".concat(result.count, " field").concat(result.count !== 1 ? 's' : '', " to AG-Refine"); + statusEl.textContent = result.message ? "\u2713 ".concat(result.message) : "\u2713 Pushed ".concat(result.pushed, " field").concat(result.pushed !== 1 ? 's' : '', " to AG-Refine"); statusEl.style.color = '#4ade80'; setTimeout(function () { statusEl.textContent = ''; @@ -3099,7 +3100,7 @@ function FieldProfileModule() { latest = log[0]; logEl.classList.remove('hidden'); logEl.innerHTML = "\n
\n
\n AG-Refine Sync Log\n ".concat(log.length, " sync").concat(log.length !== 1 ? 's' : '', "\n
\n
\n ").concat(log.slice(0, 5).map(function (entry) { - return "\n
\n
\n ".concat(new Date(entry.at).toLocaleString(), "\n
\n ").concat(entry.fieldsAdded ? "+".concat(entry.fieldsAdded, " added") : '', "\n ").concat(entry.fieldsUpdated ? "".concat(entry.fieldsAdded ? ' · ' : '').concat(entry.fieldsUpdated, " updated") : '', "\n ").concat(!entry.fieldsAdded && !entry.fieldsUpdated ? 'No changes' : '', "\n ").concat(entry.loadsFound ? " \xB7 ".concat(entry.loadsFound, " loads") : '', "\n
\n ").concat(entry.tabUrl ? "
").concat(entry.tabUrl.replace(/^https?:\/\//, '').slice(0, 40), "
") : '', "\n
\n \u2193 Pull\n
\n "); + return "\n
\n
\n ".concat(new Date(entry.at).toLocaleString(), "\n
\n ").concat(entry.fieldsAdded ? "+".concat(entry.fieldsAdded, " added") : '', "\n ").concat(entry.fieldsUpdated ? "".concat(entry.fieldsAdded ? ' · ' : '').concat(entry.fieldsUpdated, " updated") : '', "\n ").concat(!entry.fieldsAdded && !entry.fieldsUpdated ? 'No changes' : '', "\n ").concat(entry.ticketsPulled ? " \xB7 ".concat(entry.ticketsPulled, " tickets") : '', "\n ").concat(entry.labSamplesPulled ? " \xB7 ".concat(entry.labSamplesPulled, " lab samples") : '', "\n
\n ").concat(entry.tabUrl ? "
").concat(entry.tabUrl.replace(/^https?:\/\//, '').slice(0, 40), "
") : '', "\n
\n \u2193 Pull\n
\n "); }).join(''), "\n
\n ").concat(log.length > 5 ? "

".concat(log.length - 5, " older entries hidden

") : '', "\n
\n "); case 3: return _context5.a(2); @@ -3131,18 +3132,22 @@ function FieldProfileModule() { return _context8.a(2); case 3: listEl.innerHTML = profiles.map(function (p) { - var _p$cropHistory, _p$cropHistory2, _p$harvestRecords, _p$harvestRecords2, _p$cropHistory3, _p$coordinates, _p$coordinates2; + var _p$cropHistory, _p$cropHistory2, _p$labSamples, _p$labSamples2, _p$harvestRecords, _p$harvestRecords2, _p$cropHistory3, _p$coordinates, _p$coordinates2; var isExpanded = expandedId === p.id; var sourceLabel = p._source === 'ag-refine' ? 'AG-Refine' : p._source === 'ag-refine-merged' ? 'AG-Refine + manual' : p._source === 'manual' ? 'manual' : null; var cropHistoryHtml = ((_p$cropHistory = p.cropHistory) !== null && _p$cropHistory !== void 0 ? _p$cropHistory : []).length > 0 ? "
\n

Crop History

\n
\n ".concat(((_p$cropHistory2 = p.cropHistory) !== null && _p$cropHistory2 !== void 0 ? _p$cropHistory2 : []).slice(0, 5).map(function (h) { var _h$unit; return "\n
\n ".concat(h.year, " \u2014 ").concat(h.crop, "\n ").concat(h["yield"] != null ? "".concat(h["yield"], " ").concat((_h$unit = h.unit) !== null && _h$unit !== void 0 ? _h$unit : 'bu/ac', "") : '', "\n
\n "); }).join(''), "\n
\n
") : ''; + var labHtml = ((_p$labSamples = p.labSamples) !== null && _p$labSamples !== void 0 ? _p$labSamples : []).length > 0 ? "
\n

Lab / NIR Samples

\n
\n ".concat(((_p$labSamples2 = p.labSamples) !== null && _p$labSamples2 !== void 0 ? _p$labSamples2 : []).slice(0, 3).map(function (s) { + var _s$date$slice, _s$date; + return "\n
\n ".concat((_s$date$slice = (_s$date = s.date) === null || _s$date === void 0 ? void 0 : _s$date.slice(0, 10)) !== null && _s$date$slice !== void 0 ? _s$date$slice : '?', "\n \n ").concat(s.dm_pct != null ? "DM ".concat(s.dm_pct, "%") : '', "\n ").concat(s.ndf_pct != null ? " NDF ".concat(s.ndf_pct, "%") : '', "\n ").concat(s.rfv != null ? " RFV ".concat(s.rfv) : '', "\n \n
\n "); + }).join(''), "\n
\n
") : ''; var harvestHtml = ((_p$harvestRecords = p.harvestRecords) !== null && _p$harvestRecords !== void 0 ? _p$harvestRecords : []).length > 0 ? "
\n

Harvest Records

\n
\n ".concat(((_p$harvestRecords2 = p.harvestRecords) !== null && _p$harvestRecords2 !== void 0 ? _p$harvestRecords2 : []).slice(0, 4).map(function (h) { - var _h$date$slice, _h$date, _h$unit2; - return "\n
\n ".concat((_h$date$slice = (_h$date = h.date) === null || _h$date === void 0 ? void 0 : _h$date.slice(0, 10)) !== null && _h$date$slice !== void 0 ? _h$date$slice : '?', " \u2014 ").concat(h.crop, "\n \n ").concat(h["yield"] != null ? "".concat(h["yield"], " ").concat((_h$unit2 = h.unit) !== null && _h$unit2 !== void 0 ? _h$unit2 : '') : '', "\n ").concat(h.moisture != null ? " @ ".concat(h.moisture, "%") : '', "\n \n
\n "); + var _h$date$slice, _h$date, _h$unit2, _h$quality; + return "\n
\n ".concat((_h$date$slice = (_h$date = h.date) === null || _h$date === void 0 ? void 0 : _h$date.slice(0, 10)) !== null && _h$date$slice !== void 0 ? _h$date$slice : '?').concat(h.commodity ? " \u2014 ".concat(h.commodity) : h.crop ? " \u2014 ".concat(h.crop) : '', "\n \n ").concat(h["yield"] != null ? "".concat(Number(h["yield"]).toLocaleString(), " ").concat((_h$unit2 = h.unit) !== null && _h$unit2 !== void 0 ? _h$unit2 : 'lb') : '', "\n ").concat(((_h$quality = h.quality) === null || _h$quality === void 0 ? void 0 : _h$quality.dm_pct) != null ? " DM".concat(h.quality.dm_pct, "%") : h.moisture != null ? " @ ".concat(h.moisture, "%H") : '', "\n \n
\n "); }).join(''), "\n
\n
") : ''; - return "\n
\n
\n
\n

").concat(p.name, "

\n
\n ").concat(p.acres != null ? "".concat(p.acres, " ac") : '', "\n ").concat(p.soilType ? "".concat(p.soilType, "") : '', "\n ").concat(p.cluId ? "CLU ".concat(p.cluId, "") : '', "\n ").concat(((_p$cropHistory3 = p.cropHistory) !== null && _p$cropHistory3 !== void 0 ? _p$cropHistory3 : []).length > 0 ? "".concat(p.cropHistory.length, " yr history") : '', "\n ").concat(sourceLabel ? "".concat(sourceLabel, "") : '', "\n
\n
\n
\n \n \n \n \n
\n
\n\n \n
\n ").concat(((_p$coordinates = p.coordinates) === null || _p$coordinates === void 0 ? void 0 : _p$coordinates.lat) != null && ((_p$coordinates2 = p.coordinates) === null || _p$coordinates2 === void 0 ? void 0 : _p$coordinates2.lon) != null ? "

\uD83D\uDCCD ".concat(p.coordinates.lat.toFixed(4), ", ").concat(p.coordinates.lon.toFixed(4), "

") : '', "\n ").concat(p.notes ? "

\uD83D\uDCDD ".concat(p.notes, "

") : '', "\n ").concat(cropHistoryHtml, "\n ").concat(harvestHtml, "\n ").concat(!cropHistoryHtml && !harvestHtml ? "

No crop history yet \u2014 ingest a harvest file to populate.

" : '', "\n
\n

Added ").concat(new Date(p.createdAt).toLocaleDateString(), "

\n
\n ").concat(agRefineUrl ? "Open in AG-Refine \u2197") : '', "\n Carbon: Phase 7\n
\n
\n
\n
\n "); + return "\n
\n
\n
\n

").concat(p.name, "

\n
\n ").concat(p.acres != null ? "".concat(p.acres, " ac") : '', "\n ").concat(p.soilType ? "".concat(p.soilType, "") : '', "\n ").concat(p.cluId ? "CLU ".concat(p.cluId, "") : '', "\n ").concat(((_p$cropHistory3 = p.cropHistory) !== null && _p$cropHistory3 !== void 0 ? _p$cropHistory3 : []).length > 0 ? "".concat(p.cropHistory.length, " yr history") : '', "\n ").concat(sourceLabel ? "".concat(sourceLabel, "") : '', "\n
\n
\n
\n \n \n \n \n
\n
\n\n \n
\n ").concat(((_p$coordinates = p.coordinates) === null || _p$coordinates === void 0 ? void 0 : _p$coordinates.lat) != null && ((_p$coordinates2 = p.coordinates) === null || _p$coordinates2 === void 0 ? void 0 : _p$coordinates2.lon) != null ? "

\uD83D\uDCCD ".concat(p.coordinates.lat.toFixed(4), ", ").concat(p.coordinates.lon.toFixed(4), "

") : '', "\n ").concat(p.notes ? "

\uD83D\uDCDD ".concat(p.notes, "

") : '', "\n ").concat(cropHistoryHtml, "\n ").concat(harvestHtml, "\n ").concat(labHtml, "\n ").concat(!cropHistoryHtml && !harvestHtml && !labHtml ? "

No crop history yet \u2014 ingest a harvest file or pull from AG-Refine.

" : '', "\n
\n

Added ").concat(new Date(p.createdAt).toLocaleDateString(), "

\n
\n ").concat(agRefineUrl ? "Open in AG-Refine \u2197") : '', "\n Carbon: Phase 7\n
\n
\n
\n
\n "); }).join(''); listEl.querySelectorAll('.agri-card').forEach(function (card) { card.addEventListener('click', /*#__PURE__*/function () { @@ -3447,33 +3452,34 @@ __webpack_require__.r(__webpack_exports__); /* harmony export */ syncFromAgRefine: () => (/* binding */ syncFromAgRefine) /* harmony export */ }); /* harmony import */ var _storage_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./storage.js */ "./src/utils/storage.js"); +function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); } function _regeneratorValues(e) { if (null != e) { var t = e["function" == typeof Symbol && Symbol.iterator || "@@iterator"], r = 0; if (t) return t.call(e); if ("function" == typeof e.next) return e; if (!isNaN(e.length)) return { next: function next() { return e && r >= e.length && (e = void 0), { value: e && e[r++], done: !e }; } }; } throw new TypeError(_typeof(e) + " is not iterable"); } -function _regenerator() { /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/babel/babel/blob/main/packages/babel-helpers/LICENSE */ var e, t, r = "function" == typeof Symbol ? Symbol : {}, n = r.iterator || "@@iterator", o = r.toStringTag || "@@toStringTag"; function i(r, n, o, i) { var c = n && n.prototype instanceof Generator ? n : Generator, u = Object.create(c.prototype); return _regeneratorDefine2(u, "_invoke", function (r, n, o) { var i, c, u, f = 0, p = o || [], y = !1, G = { p: 0, n: 0, v: e, a: d, f: d.bind(e, 4), d: function d(t, r) { return i = t, c = 0, u = e, G.n = r, a; } }; function d(r, n) { for (c = r, u = n, t = 0; !y && f && !o && t < p.length; t++) { var o, i = p[t], d = G.p, l = i[2]; r > 3 ? (o = l === n) && (u = i[(c = i[4]) ? 5 : (c = 3, 3)], i[4] = i[5] = e) : i[0] <= d && ((o = r < 2 && d < i[1]) ? (c = 0, G.v = n, G.n = i[1]) : d < l && (o = r < 3 || i[0] > n || n > l) && (i[4] = r, i[5] = n, G.n = l, c = 0)); } if (o || r > 1) return a; throw y = !0, n; } return function (o, p, l) { if (f > 1) throw TypeError("Generator is already running"); for (y && 1 === p && d(p, l), c = p, u = l; (t = c < 2 ? e : u) || !y;) { i || (c ? c < 3 ? (c > 1 && (G.n = -1), d(c, u)) : G.n = u : G.v = u); try { if (f = 2, i) { if (c || (o = "next"), t = i[o]) { if (!(t = t.call(i, u))) throw TypeError("iterator result is not an object"); if (!t.done) return t; u = t.value, c < 2 && (c = 0); } else 1 === c && (t = i["return"]) && t.call(i), c < 2 && (u = TypeError("The iterator does not provide a '" + o + "' method"), c = 1); i = e; } else if ((t = (y = G.n < 0) ? u : r.call(n, G)) !== a) break; } catch (t) { i = e, c = 1, u = t; } finally { f = 1; } } return { value: t, done: y }; }; }(r, o, i), !0), u; } var a = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} t = Object.getPrototypeOf; var c = [][n] ? t(t([][n]())) : (_regeneratorDefine2(t = {}, n, function () { return this; }), t), u = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(c); function f(e) { return Object.setPrototypeOf ? Object.setPrototypeOf(e, GeneratorFunctionPrototype) : (e.__proto__ = GeneratorFunctionPrototype, _regeneratorDefine2(e, o, "GeneratorFunction")), e.prototype = Object.create(u), e; } return GeneratorFunction.prototype = GeneratorFunctionPrototype, _regeneratorDefine2(u, "constructor", GeneratorFunctionPrototype), _regeneratorDefine2(GeneratorFunctionPrototype, "constructor", GeneratorFunction), GeneratorFunction.displayName = "GeneratorFunction", _regeneratorDefine2(GeneratorFunctionPrototype, o, "GeneratorFunction"), _regeneratorDefine2(u), _regeneratorDefine2(u, o, "Generator"), _regeneratorDefine2(u, n, function () { return this; }), _regeneratorDefine2(u, "toString", function () { return "[object Generator]"; }), (_regenerator = function _regenerator() { return { w: i, m: f }; })(); } -function _regeneratorDefine2(e, r, n, t) { var i = Object.defineProperty; try { i({}, "", {}); } catch (e) { i = 0; } _regeneratorDefine2 = function _regeneratorDefine(e, r, n, t) { function o(r, n) { _regeneratorDefine2(e, r, function (e) { return this._invoke(r, n, e); }); } r ? i ? i(e, r, { value: n, enumerable: !t, configurable: !t, writable: !t }) : e[r] = n : (o("next", 0), o("throw", 1), o("return", 2)); }, _regeneratorDefine2(e, r, n, t); } +function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } +function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } +function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; } +function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; } +function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t["return"] || t["return"](); } finally { if (u) throw o; } } }; } -function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); } function _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); } function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } } function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; } function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t["return"] && (u = t["return"](), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } } function _arrayWithHoles(r) { if (Array.isArray(r)) return r; } -function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } -function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } -function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; } -function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; } -function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } +function _regenerator() { /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/babel/babel/blob/main/packages/babel-helpers/LICENSE */ var e, t, r = "function" == typeof Symbol ? Symbol : {}, n = r.iterator || "@@iterator", o = r.toStringTag || "@@toStringTag"; function i(r, n, o, i) { var c = n && n.prototype instanceof Generator ? n : Generator, u = Object.create(c.prototype); return _regeneratorDefine2(u, "_invoke", function (r, n, o) { var i, c, u, f = 0, p = o || [], y = !1, G = { p: 0, n: 0, v: e, a: d, f: d.bind(e, 4), d: function d(t, r) { return i = t, c = 0, u = e, G.n = r, a; } }; function d(r, n) { for (c = r, u = n, t = 0; !y && f && !o && t < p.length; t++) { var o, i = p[t], d = G.p, l = i[2]; r > 3 ? (o = l === n) && (u = i[(c = i[4]) ? 5 : (c = 3, 3)], i[4] = i[5] = e) : i[0] <= d && ((o = r < 2 && d < i[1]) ? (c = 0, G.v = n, G.n = i[1]) : d < l && (o = r < 3 || i[0] > n || n > l) && (i[4] = r, i[5] = n, G.n = l, c = 0)); } if (o || r > 1) return a; throw y = !0, n; } return function (o, p, l) { if (f > 1) throw TypeError("Generator is already running"); for (y && 1 === p && d(p, l), c = p, u = l; (t = c < 2 ? e : u) || !y;) { i || (c ? c < 3 ? (c > 1 && (G.n = -1), d(c, u)) : G.n = u : G.v = u); try { if (f = 2, i) { if (c || (o = "next"), t = i[o]) { if (!(t = t.call(i, u))) throw TypeError("iterator result is not an object"); if (!t.done) return t; u = t.value, c < 2 && (c = 0); } else 1 === c && (t = i["return"]) && t.call(i), c < 2 && (u = TypeError("The iterator does not provide a '" + o + "' method"), c = 1); i = e; } else if ((t = (y = G.n < 0) ? u : r.call(n, G)) !== a) break; } catch (t) { i = e, c = 1, u = t; } finally { f = 1; } } return { value: t, done: y }; }; }(r, o, i), !0), u; } var a = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} t = Object.getPrototypeOf; var c = [][n] ? t(t([][n]())) : (_regeneratorDefine2(t = {}, n, function () { return this; }), t), u = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(c); function f(e) { return Object.setPrototypeOf ? Object.setPrototypeOf(e, GeneratorFunctionPrototype) : (e.__proto__ = GeneratorFunctionPrototype, _regeneratorDefine2(e, o, "GeneratorFunction")), e.prototype = Object.create(u), e; } return GeneratorFunction.prototype = GeneratorFunctionPrototype, _regeneratorDefine2(u, "constructor", GeneratorFunctionPrototype), _regeneratorDefine2(GeneratorFunctionPrototype, "constructor", GeneratorFunction), GeneratorFunction.displayName = "GeneratorFunction", _regeneratorDefine2(GeneratorFunctionPrototype, o, "GeneratorFunction"), _regeneratorDefine2(u), _regeneratorDefine2(u, o, "Generator"), _regeneratorDefine2(u, n, function () { return this; }), _regeneratorDefine2(u, "toString", function () { return "[object Generator]"; }), (_regenerator = function _regenerator() { return { w: i, m: f }; })(); } +function _regeneratorDefine2(e, r, n, t) { var i = Object.defineProperty; try { i({}, "", {}); } catch (e) { i = 0; } _regeneratorDefine2 = function _regeneratorDefine(e, r, n, t) { function o(r, n) { _regeneratorDefine2(e, r, function (e) { return this._invoke(r, n, e); }); } r ? i ? i(e, r, { value: n, enumerable: !t, configurable: !t, writable: !t }) : e[r] = n : (o("next", 0), o("throw", 1), o("return", 2)); }, _regeneratorDefine2(e, r, n, t); } function asyncGeneratorStep(n, t, e, r, o, a, c) { try { var i = n[a](c), u = i.value; } catch (n) { return void e(n); } i.done ? t(u) : Promise.resolve(u).then(r, o); } function _asyncToGenerator(n) { return function () { var t = this, e = arguments; return new Promise(function (r, o) { var a = n.apply(t, e); function _next(n) { asyncGeneratorStep(a, r, o, _next, _throw, "next", n); } function _throw(n) { asyncGeneratorStep(a, r, o, _next, _throw, "throw", n); } _next(void 0); }); }; } /** * AG-Refine Sister-App Bridge * - * Detects an open AG-Refine tab, pulls field and output data from its - * localStorage/sessionStorage, and maps it into Agrifine field profiles. + * Calls AG-Refine's REST API through the tab's own page context so session + * cookies work without any CORS setup on the server. Requires AG-Refine to + * be open in a browser tab and the user to be logged in there. * - * AG-Refine tab detection: any tab whose URL matches a configurable pattern - * (default: localhost:* OR any URL containing "ag-refine" or "agrefine"). - * Set the URL in Settings > AG-Refine URL to pin it to a specific origin. + * Tab detection: matches any tab whose URL starts with the configured base URL + * (default: localhost:* or any URL containing "ag-refine" / "agrefine"). + * Set Settings → AG-Refine URL to pin it to a specific origin. */ @@ -3581,353 +3587,626 @@ function tabMatchesAgRefine(tab, configuredUrl) { var u = tab.url.toLowerCase(); return u.includes('ag-refine') || u.includes('agrefine') || u.startsWith('http://localhost') || u.startsWith('http://127.0.0.1'); } - -// Injected into the AG-Refine tab — reads all storage and DOM hints -function scrapeAgRefineTab() { - var out = { - localStorage: {}, - sessionStorage: {}, - domHints: {} - }; - for (var i = 0; i < localStorage.length; i++) { - var k = localStorage.key(i); - try { - out.localStorage[k] = JSON.parse(localStorage.getItem(k)); - } catch (_) { - out.localStorage[k] = localStorage.getItem(k); - } - } - for (var _i = 0; _i < sessionStorage.length; _i++) { - var _k = sessionStorage.key(_i); - try { - out.sessionStorage[_k] = JSON.parse(sessionStorage.getItem(_k)); - } catch (_) { - out.sessionStorage[_k] = sessionStorage.getItem(_k); - } - } - - // Pull field-name-like text from the DOM as a fallback hint - var fieldEls = document.querySelectorAll('[data-field],[data-name],[data-id]'); - fieldEls.forEach(function (el) { - var _ref, _el$dataset$field, _el$textContent; - var id = (_ref = (_el$dataset$field = el.dataset.field) !== null && _el$dataset$field !== void 0 ? _el$dataset$field : el.dataset.id) !== null && _ref !== void 0 ? _ref : el.dataset.name; - if (id) out.domHints[id] = ((_el$textContent = el.textContent) !== null && _el$textContent !== void 0 ? _el$textContent : '').trim().slice(0, 200); - }); - return out; -} - -/** - * Map raw AG-Refine storage dump to Agrifine field profile shape. - * Tries common key patterns used by React/Next.js ag apps. - */ -function extractFields(raw) { - var all = _objectSpread(_objectSpread({}, raw.localStorage), raw.sessionStorage); - var candidates = []; - for (var _i2 = 0, _Object$entries = Object.entries(all); _i2 < _Object$entries.length; _i2++) { - var _Object$entries$_i = _slicedToArray(_Object$entries[_i2], 2), - key = _Object$entries$_i[0], - val = _Object$entries$_i[1]; - var k = key.toLowerCase(); - if (!k.includes('field') && !k.includes('load') && !k.includes('farm') && !k.includes('plot')) continue; - var arr = Array.isArray(val) ? val : val && _typeof(val) === 'object' ? [val] : null; - if (!arr) continue; - var _iterator = _createForOfIteratorHelper(arr), - _step; - try { - for (_iterator.s(); !(_step = _iterator.n()).done;) { - var _ref2, _ref3, _ref4, _ref5, _item$name, _ref6, _item$id, _ref7, _ref8, _item$cluId, _ref9, _ref0, _item$acres, _ref1, _ref10, _item$soilType, _ref11, _item$lat, _item$coordinates, _ref12, _ref13, _ref14, _item$lon, _item$coordinates2, _item$coordinates3, _ref15, _ref16, _item$notes, _item$cropHistory, _item$cropHistory2, _item$harvests, _item$harvests2, _item$carbonPotential, _ref17, _item$createdAt; - var item = _step.value; - if (!item || _typeof(item) !== 'object') continue; - var name = (_ref2 = (_ref3 = (_ref4 = (_ref5 = (_item$name = item.name) !== null && _item$name !== void 0 ? _item$name : item.fieldName) !== null && _ref5 !== void 0 ? _ref5 : item.field_name) !== null && _ref4 !== void 0 ? _ref4 : item.title) !== null && _ref3 !== void 0 ? _ref3 : item.label) !== null && _ref2 !== void 0 ? _ref2 : null; - if (!name) continue; - candidates.push({ - id: "agr_".concat((_ref6 = (_item$id = item.id) !== null && _item$id !== void 0 ? _item$id : item.fieldId) !== null && _ref6 !== void 0 ? _ref6 : Date.now(), "_").concat(Math.random().toString(36).slice(2, 6)), - name: String(name), - cluId: (_ref7 = (_ref8 = (_item$cluId = item.cluId) !== null && _item$cluId !== void 0 ? _item$cluId : item.clu_id) !== null && _ref8 !== void 0 ? _ref8 : item.clu) !== null && _ref7 !== void 0 ? _ref7 : null, - acres: parseFloat((_ref9 = (_ref0 = (_item$acres = item.acres) !== null && _item$acres !== void 0 ? _item$acres : item.area) !== null && _ref0 !== void 0 ? _ref0 : item.size) !== null && _ref9 !== void 0 ? _ref9 : item.acreage) || null, - soilType: (_ref1 = (_ref10 = (_item$soilType = item.soilType) !== null && _item$soilType !== void 0 ? _item$soilType : item.soil_type) !== null && _ref10 !== void 0 ? _ref10 : item.soil) !== null && _ref1 !== void 0 ? _ref1 : null, - coordinates: { - lat: parseFloat((_ref11 = (_item$lat = item.lat) !== null && _item$lat !== void 0 ? _item$lat : item.latitude) !== null && _ref11 !== void 0 ? _ref11 : (_item$coordinates = item.coordinates) === null || _item$coordinates === void 0 ? void 0 : _item$coordinates.lat) || null, - lon: parseFloat((_ref12 = (_ref13 = (_ref14 = (_item$lon = item.lon) !== null && _item$lon !== void 0 ? _item$lon : item.lng) !== null && _ref14 !== void 0 ? _ref14 : item.longitude) !== null && _ref13 !== void 0 ? _ref13 : (_item$coordinates2 = item.coordinates) === null || _item$coordinates2 === void 0 ? void 0 : _item$coordinates2.lon) !== null && _ref12 !== void 0 ? _ref12 : (_item$coordinates3 = item.coordinates) === null || _item$coordinates3 === void 0 ? void 0 : _item$coordinates3.lng) || null - }, - notes: (_ref15 = (_ref16 = (_item$notes = item.notes) !== null && _item$notes !== void 0 ? _item$notes : item.description) !== null && _ref16 !== void 0 ? _ref16 : item.comments) !== null && _ref15 !== void 0 ? _ref15 : null, - cropHistory: Array.isArray((_item$cropHistory = item.cropHistory) !== null && _item$cropHistory !== void 0 ? _item$cropHistory : item.crop_history) ? (_item$cropHistory2 = item.cropHistory) !== null && _item$cropHistory2 !== void 0 ? _item$cropHistory2 : item.crop_history : [], - harvestRecords: Array.isArray((_item$harvests = item.harvests) !== null && _item$harvests !== void 0 ? _item$harvests : item.harvestRecords) ? (_item$harvests2 = item.harvests) !== null && _item$harvests2 !== void 0 ? _item$harvests2 : item.harvestRecords : [], - carbonPotential: (_item$carbonPotential = item.carbonPotential) !== null && _item$carbonPotential !== void 0 ? _item$carbonPotential : null, - weatherData: null, - createdAt: (_ref17 = (_item$createdAt = item.createdAt) !== null && _item$createdAt !== void 0 ? _item$createdAt : item.created_at) !== null && _ref17 !== void 0 ? _ref17 : new Date().toISOString(), - _source: 'ag-refine' - }); - } - } catch (err) { - _iterator.e(err); - } finally { - _iterator.f(); - } - } - return candidates; -} - -/** - * Loads also come over — map to ingested file records for the dashboard. - */ -function extractLoads(raw) { - var all = _objectSpread(_objectSpread({}, raw.localStorage), raw.sessionStorage); - var loads = []; - for (var _i3 = 0, _Object$entries2 = Object.entries(all); _i3 < _Object$entries2.length; _i3++) { - var _Object$entries2$_i = _slicedToArray(_Object$entries2[_i3], 2), - key = _Object$entries2$_i[0], - val = _Object$entries2$_i[1]; - var k = key.toLowerCase(); - if (!k.includes('load') && !k.includes('scale') && !k.includes('ticket') && !k.includes('delivery')) continue; - var arr = Array.isArray(val) ? val : null; - if (!arr) continue; - var _iterator2 = _createForOfIteratorHelper(arr), - _step2; - try { - for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) { - var item = _step2.value; - if (!item || _typeof(item) !== 'object') continue; - loads.push(item); - } - } catch (err) { - _iterator2.e(err); - } finally { - _iterator2.f(); - } - } - return loads; -} - -// Injected into AG-Refine tab to write field data back into its localStorage -function writeFieldsToAgRefineTab(fields) { - try { - localStorage.setItem('agrifine_pushed_fields', JSON.stringify(fields)); - localStorage.setItem('agrifine_pushed_at', new Date().toISOString()); - // Dispatch an event so a listening AG-Refine app can react immediately - window.dispatchEvent(new CustomEvent('agrifine:fields-updated', { - detail: { - fields: fields - } - })); - return { - ok: true, - count: fields.length - }; - } catch (err) { - return { - ok: false, - error: err.message - }; - } -} -function pushToAgRefine(_x2) { - return _pushToAgRefine.apply(this, arguments); -} -function _pushToAgRefine() { - _pushToAgRefine = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee4(profiles) { - var configuredUrl, allTabs, agRefineTabs, tab, _yield$chrome$scripti, _yield$chrome$scripti2, result, _t7; +function findAgRefineTab(_x2) { + return _findAgRefineTab.apply(this, arguments); +} // ── Functions injected into the AG-Refine tab ───────────────────────────────── +// These run in the page's origin context — same-origin, session cookies included. +// They must be fully self-contained (no closures over outer-scope variables). +function _findAgRefineTab() { + _findAgRefineTab = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee4(configuredUrl) { + var _allTabs$find; + var allTabs; return _regenerator().w(function (_context4) { - while (1) switch (_context4.p = _context4.n) { + while (1) switch (_context4.n) { case 0: _context4.n = 1; - return getAgRefineUrl(); - case 1: - configuredUrl = _context4.v; - _context4.n = 2; return chrome.tabs.query({}); - case 2: + case 1: allTabs = _context4.v; - agRefineTabs = allTabs.filter(function (t) { + return _context4.a(2, (_allTabs$find = allTabs.find(function (t) { return tabMatchesAgRefine(t, configuredUrl); + })) !== null && _allTabs$find !== void 0 ? _allTabs$find : null); + } + }, _callee4); + })); + return _findAgRefineTab.apply(this, arguments); +} +function _injectFetchAll() { + return _injectFetchAll2.apply(this, arguments); +} +function _injectFetchAll2() { + _injectFetchAll2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee6() { + var base, get, _get, _yield$Promise$all, _yield$Promise$all2, fields, tickets, labSamples, harvestPlans; + return _regenerator().w(function (_context6) { + while (1) switch (_context6.n) { + case 0: + _get = function _get3() { + _get = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee5(path) { + var res, _t7; + return _regenerator().w(function (_context5) { + while (1) switch (_context5.p = _context5.n) { + case 0: + _context5.p = 0; + _context5.n = 1; + return fetch(base + path, { + credentials: 'include' + }); + case 1: + res = _context5.v; + if (!(res.status === 401)) { + _context5.n = 2; + break; + } + return _context5.a(2, { + __auth_error: true + }); + case 2: + return _context5.a(2, res.ok ? res.json() : null); + case 3: + _context5.p = 3; + _t7 = _context5.v; + return _context5.a(2, null); + } + }, _callee5, null, [[0, 3]]); + })); + return _get.apply(this, arguments); + }; + get = function _get2(_x5) { + return _get.apply(this, arguments); + }; + base = window.location.origin; + _context6.n = 1; + return Promise.all([get('/api/fields/'), get('/api/scales/tickets/all'), get('/api/intelligence/lab-samples'), get('/api/harvest/plans')]); + case 1: + _yield$Promise$all = _context6.v; + _yield$Promise$all2 = _slicedToArray(_yield$Promise$all, 4); + fields = _yield$Promise$all2[0]; + tickets = _yield$Promise$all2[1]; + labSamples = _yield$Promise$all2[2]; + harvestPlans = _yield$Promise$all2[3]; + return _context6.a(2, { + fields: fields, + tickets: tickets, + labSamples: labSamples, + harvestPlans: harvestPlans }); - if (!(agRefineTabs.length === 0)) { - _context4.n = 3; + } + }, _callee6); + })); + return _injectFetchAll2.apply(this, arguments); +} +function _injectPushFields(_x3) { + return _injectPushFields2.apply(this, arguments); +} // ── Data mapping ────────────────────────────────────────────────────────────── +function _injectPushFields2() { + _injectPushFields2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee7(newFields) { + var base, results, _iterator, _step, field, _body$id, res, body, _t8, _t9, _t0; + return _regenerator().w(function (_context7) { + while (1) switch (_context7.p = _context7.n) { + case 0: + base = window.location.origin; + results = []; + _iterator = _createForOfIteratorHelper(newFields); + _context7.p = 1; + _iterator.s(); + case 2: + if ((_step = _iterator.n()).done) { + _context7.n = 10; break; } - return _context4.a(2, { - ok: false, - error: 'No AG-Refine tab found. Open AG-Refine first.' - }); - case 3: - tab = agRefineTabs[0]; - _context4.p = 4; - _context4.n = 5; - return chrome.scripting.executeScript({ - target: { - tabId: tab.id + field = _step.value; + _context7.p = 3; + _context7.n = 4; + return fetch(base + '/api/fields/', { + method: 'POST', + credentials: 'include', + headers: { + 'Content-Type': 'application/json' }, - func: writeFieldsToAgRefineTab, - args: [profiles] + body: JSON.stringify(field) }); + case 4: + res = _context7.v; + if (!res.ok) { + _context7.n = 6; + break; + } + _context7.n = 5; + return res.json(); case 5: - _yield$chrome$scripti = _context4.v; - _yield$chrome$scripti2 = _slicedToArray(_yield$chrome$scripti, 1); - result = _yield$chrome$scripti2[0]; - return _context4.a(2, result.result); + _t8 = _context7.v; + _context7.n = 7; + break; case 6: - _context4.p = 6; - _t7 = _context4.v; - return _context4.a(2, { + _t8 = null; + case 7: + body = _t8; + results.push({ + name: field.name, + ok: res.ok, + status: res.status, + agRefineId: (_body$id = body === null || body === void 0 ? void 0 : body.id) !== null && _body$id !== void 0 ? _body$id : null + }); + _context7.n = 9; + break; + case 8: + _context7.p = 8; + _t9 = _context7.v; + results.push({ + name: field.name, ok: false, - error: "Cannot write to AG-Refine tab: ".concat(_t7.message) + status: 0, + error: _t9.message }); + case 9: + _context7.n = 2; + break; + case 10: + _context7.n = 12; + break; + case 11: + _context7.p = 11; + _t0 = _context7.v; + _iterator.e(_t0); + case 12: + _context7.p = 12; + _iterator.f(); + return _context7.f(12); + case 13: + return _context7.a(2, results); } - }, _callee4, null, [[4, 6]]); + }, _callee7, null, [[3, 8], [1, 11, 12, 13]]); })); - return _pushToAgRefine.apply(this, arguments); + return _injectPushFields2.apply(this, arguments); } +function mapAgFieldToProfile(agField, ticketsByFieldId, labSamplesByFieldId) { + var _ticketsByFieldId$agF, _labSamplesByFieldId$, _agField$farm_name, _ref, _agField$acres_fsa, _agField$notes; + var fieldTickets = ((_ticketsByFieldId$agF = ticketsByFieldId[agField.id]) !== null && _ticketsByFieldId$agF !== void 0 ? _ticketsByFieldId$agF : []).filter(function (t) { + return t.net_weight != null; + }).map(function (t) { + var _t$unit, _t$dry_matter_pct, _t$protein_pct, _t$starch_pct, _t$commodity, _t$harvest; + return { + date: t.captured_at, + "yield": t.net_weight, + unit: (_t$unit = t.unit) !== null && _t$unit !== void 0 ? _t$unit : 'lb', + moisture: t.dry_matter_pct != null ? +(100 - t.dry_matter_pct).toFixed(1) : null, + quality: { + dm_pct: (_t$dry_matter_pct = t.dry_matter_pct) !== null && _t$dry_matter_pct !== void 0 ? _t$dry_matter_pct : null, + protein_pct: (_t$protein_pct = t.protein_pct) !== null && _t$protein_pct !== void 0 ? _t$protein_pct : null, + starch_pct: (_t$starch_pct = t.starch_pct) !== null && _t$starch_pct !== void 0 ? _t$starch_pct : null + }, + commodity: (_t$commodity = t.commodity) !== null && _t$commodity !== void 0 ? _t$commodity : null, + harvest_label: (_t$harvest = t.harvest) !== null && _t$harvest !== void 0 ? _t$harvest : null, + ticket_id: t.id, + source: 'ag-refine' + }; + }); + var cropHistory = []; + if (agField.crop) { + var _agField$variety; + var year = agField.planting_date ? parseInt(agField.planting_date.slice(0, 4), 10) : new Date().getFullYear(); + cropHistory.push({ + year: year, + crop: agField.crop, + variety: (_agField$variety = agField.variety) !== null && _agField$variety !== void 0 ? _agField$variety : null + }); + } + var labSamples = ((_labSamplesByFieldId$ = labSamplesByFieldId[agField.id]) !== null && _labSamplesByFieldId$ !== void 0 ? _labSamplesByFieldId$ : []).map(function (s) { + var _s$sampled_at, _s$dry_matter_pct, _s$ndf_pct, _s$adf_pct, _s$rfv, _s$rfq, _s$nel, _s$digestibility_pct; + return { + date: (_s$sampled_at = s.sampled_at) !== null && _s$sampled_at !== void 0 ? _s$sampled_at : null, + dm_pct: (_s$dry_matter_pct = s.dry_matter_pct) !== null && _s$dry_matter_pct !== void 0 ? _s$dry_matter_pct : null, + ndf_pct: (_s$ndf_pct = s.ndf_pct) !== null && _s$ndf_pct !== void 0 ? _s$ndf_pct : null, + adf_pct: (_s$adf_pct = s.adf_pct) !== null && _s$adf_pct !== void 0 ? _s$adf_pct : null, + rfv: (_s$rfv = s.rfv) !== null && _s$rfv !== void 0 ? _s$rfv : null, + rfq: (_s$rfq = s.rfq) !== null && _s$rfq !== void 0 ? _s$rfq : null, + nel: (_s$nel = s.nel) !== null && _s$nel !== void 0 ? _s$nel : null, + digestibility_pct: (_s$digestibility_pct = s.digestibility_pct) !== null && _s$digestibility_pct !== void 0 ? _s$digestibility_pct : null + }; + }); + return { + id: "agr_".concat(agField.id), + name: agField.name, + farmName: (_agField$farm_name = agField.farm_name) !== null && _agField$farm_name !== void 0 ? _agField$farm_name : null, + acres: (_ref = (_agField$acres_fsa = agField.acres_fsa) !== null && _agField$acres_fsa !== void 0 ? _agField$acres_fsa : agField.acres_calculated) !== null && _ref !== void 0 ? _ref : null, + soilType: null, + coordinates: { + lat: null, + lon: null + }, + notes: (_agField$notes = agField.notes) !== null && _agField$notes !== void 0 ? _agField$notes : null, + cropHistory: cropHistory, + harvestRecords: fieldTickets, + labSamples: labSamples, + carbonPotential: null, + weatherData: null, + createdAt: new Date().toISOString(), + _source: 'ag-refine', + _agRefineId: agField.id + }; +} + +// ── Public API ──────────────────────────────────────────────────────────────── + function syncFromAgRefine() { return _syncFromAgRefine.apply(this, arguments); } function _syncFromAgRefine() { - _syncFromAgRefine = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee5() { - var configuredUrl, allTabs, agRefineTabs, tab, raw, _yield$chrome$scripti3, _yield$chrome$scripti4, result, fields, loads, existing, added, updated, _iterator3, _step3, _loop, log, history, _t8, _t9; - return _regenerator().w(function (_context6) { - while (1) switch (_context6.p = _context6.n) { + _syncFromAgRefine = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee8() { + var _raw$fields; + var configuredUrl, tab, raw, _yield$chrome$scripti, _yield$chrome$scripti2, result, agFields, allTickets, allLabSamples, ticketsByFieldId, _iterator2, _step2, _t$field_id, _ticketsByFieldId$_t$, t, labSamplesByFieldId, _iterator3, _step3, _s$field_id, _labSamplesByFieldId$2, s, incomingProfiles, existing, added, updated, _iterator4, _step4, _loop, logEntry, history, _t1, _t10, _t11, _t12; + return _regenerator().w(function (_context9) { + while (1) switch (_context9.p = _context9.n) { case 0: - _context6.n = 1; + _context9.n = 1; return getAgRefineUrl(); case 1: - configuredUrl = _context6.v; - _context6.n = 2; - return chrome.tabs.query({}); + configuredUrl = _context9.v; + _context9.n = 2; + return findAgRefineTab(configuredUrl); case 2: - allTabs = _context6.v; - agRefineTabs = allTabs.filter(function (t) { - return tabMatchesAgRefine(t, configuredUrl); - }); - if (!(agRefineTabs.length === 0)) { - _context6.n = 3; + tab = _context9.v; + if (tab) { + _context9.n = 3; break; } - return _context6.a(2, { + return _context9.a(2, { ok: false, error: 'No AG-Refine tab found. Open AG-Refine in a browser tab first.' }); case 3: - tab = agRefineTabs[0]; - _context6.p = 4; - _context6.n = 5; + _context9.p = 3; + _context9.n = 4; return chrome.scripting.executeScript({ target: { tabId: tab.id }, - func: scrapeAgRefineTab + func: _injectFetchAll }); - case 5: - _yield$chrome$scripti3 = _context6.v; - _yield$chrome$scripti4 = _slicedToArray(_yield$chrome$scripti3, 1); - result = _yield$chrome$scripti4[0]; + case 4: + _yield$chrome$scripti = _context9.v; + _yield$chrome$scripti2 = _slicedToArray(_yield$chrome$scripti, 1); + result = _yield$chrome$scripti2[0]; raw = result.result; - _context6.n = 7; + _context9.n = 6; break; + case 5: + _context9.p = 5; + _t1 = _context9.v; + return _context9.a(2, { + ok: false, + error: "Cannot reach AG-Refine tab: ".concat(_t1.message) + }); case 6: - _context6.p = 6; - _t8 = _context6.v; - return _context6.a(2, { + if (!(!raw || (_raw$fields = raw.fields) !== null && _raw$fields !== void 0 && _raw$fields.__auth_error)) { + _context9.n = 7; + break; + } + return _context9.a(2, { ok: false, - error: "Cannot read AG-Refine tab: ".concat(_t8.message) + error: 'AG-Refine returned 401 — please log in to AG-Refine first.' }); case 7: - fields = extractFields(raw); - loads = extractLoads(raw); // Merge fields — update existing by name, insert new ones - _context6.n = 8; + agFields = Array.isArray(raw.fields) ? raw.fields.filter(function (f) { + return f.active !== false; + }) : []; + allTickets = Array.isArray(raw.tickets) ? raw.tickets : []; + allLabSamples = Array.isArray(raw.labSamples) ? raw.labSamples : []; // Index by field_id for O(1) lookup + ticketsByFieldId = {}; + _iterator2 = _createForOfIteratorHelper(allTickets); + _context9.p = 8; + _iterator2.s(); + case 9: + if ((_step2 = _iterator2.n()).done) { + _context9.n = 12; + break; + } + t = _step2.value; + if (!(t.field_id == null)) { + _context9.n = 10; + break; + } + return _context9.a(3, 11); + case 10: + ((_ticketsByFieldId$_t$ = ticketsByFieldId[_t$field_id = t.field_id]) !== null && _ticketsByFieldId$_t$ !== void 0 ? _ticketsByFieldId$_t$ : ticketsByFieldId[_t$field_id] = []).push(t); + case 11: + _context9.n = 9; + break; + case 12: + _context9.n = 14; + break; + case 13: + _context9.p = 13; + _t10 = _context9.v; + _iterator2.e(_t10); + case 14: + _context9.p = 14; + _iterator2.f(); + return _context9.f(14); + case 15: + labSamplesByFieldId = {}; + _iterator3 = _createForOfIteratorHelper(allLabSamples); + _context9.p = 16; + _iterator3.s(); + case 17: + if ((_step3 = _iterator3.n()).done) { + _context9.n = 20; + break; + } + s = _step3.value; + if (!(s.field_id == null)) { + _context9.n = 18; + break; + } + return _context9.a(3, 19); + case 18: + ((_labSamplesByFieldId$2 = labSamplesByFieldId[_s$field_id = s.field_id]) !== null && _labSamplesByFieldId$2 !== void 0 ? _labSamplesByFieldId$2 : labSamplesByFieldId[_s$field_id] = []).push(s); + case 19: + _context9.n = 17; + break; + case 20: + _context9.n = 22; + break; + case 21: + _context9.p = 21; + _t11 = _context9.v; + _iterator3.e(_t11); + case 22: + _context9.p = 22; + _iterator3.f(); + return _context9.f(22); + case 23: + incomingProfiles = agFields.map(function (f) { + return mapAgFieldToProfile(f, ticketsByFieldId, labSamplesByFieldId); + }); + _context9.n = 24; return (0,_storage_js__WEBPACK_IMPORTED_MODULE_0__.getFieldProfiles)(); - case 8: - existing = _context6.v; + case 24: + existing = _context9.v; added = 0; updated = 0; - _iterator3 = _createForOfIteratorHelper(fields); - _context6.p = 9; + _iterator4 = _createForOfIteratorHelper(incomingProfiles); + _context9.p = 25; _loop = /*#__PURE__*/_regenerator().m(function _loop() { - var f, match, _match$coordinates, _match$cropHistory, _match$notes, _match$cluId, merged; - return _regenerator().w(function (_context5) { - while (1) switch (_context5.n) { + var _existing$find; + var profile, match, _match$cropHistory, _match$harvestRecords, _match$soilType, _match$coordinates, _match$notes; + return _regenerator().w(function (_context8) { + while (1) switch (_context8.n) { case 0: - f = _step3.value; - match = existing.find(function (e) { - return e.name.toLowerCase() === f.name.toLowerCase(); + profile = _step4.value; + match = (_existing$find = existing.find(function (e) { + return e._agRefineId === profile._agRefineId; + })) !== null && _existing$find !== void 0 ? _existing$find : existing.find(function (e) { + return e.name.toLowerCase() === profile.name.toLowerCase(); }); if (!match) { - _context5.n = 2; + _context8.n = 2; break; } - // Merge: fill in missing data without overwriting user edits - merged = _objectSpread(_objectSpread(_objectSpread({}, f), match), {}, { - coordinates: ((_match$coordinates = match.coordinates) === null || _match$coordinates === void 0 ? void 0 : _match$coordinates.lat) != null ? match.coordinates : f.coordinates, - cropHistory: (_match$cropHistory = match.cropHistory) !== null && _match$cropHistory !== void 0 && _match$cropHistory.length ? match.cropHistory : f.cropHistory, - notes: (_match$notes = match.notes) !== null && _match$notes !== void 0 ? _match$notes : f.notes, - cluId: (_match$cluId = match.cluId) !== null && _match$cluId !== void 0 ? _match$cluId : f.cluId, + _context8.n = 1; + return (0,_storage_js__WEBPACK_IMPORTED_MODULE_0__.saveFieldProfile)(_objectSpread(_objectSpread({}, profile), {}, { + id: match.id, + cropHistory: (_match$cropHistory = match.cropHistory) !== null && _match$cropHistory !== void 0 && _match$cropHistory.length ? match.cropHistory : profile.cropHistory, + harvestRecords: profile.harvestRecords.length ? profile.harvestRecords : (_match$harvestRecords = match.harvestRecords) !== null && _match$harvestRecords !== void 0 ? _match$harvestRecords : [], + soilType: (_match$soilType = match.soilType) !== null && _match$soilType !== void 0 ? _match$soilType : profile.soilType, + coordinates: ((_match$coordinates = match.coordinates) === null || _match$coordinates === void 0 ? void 0 : _match$coordinates.lat) != null ? match.coordinates : profile.coordinates, + notes: (_match$notes = match.notes) !== null && _match$notes !== void 0 ? _match$notes : profile.notes, _source: 'ag-refine-merged' - }); - _context5.n = 1; - return (0,_storage_js__WEBPACK_IMPORTED_MODULE_0__.saveFieldProfile)(merged); + })); case 1: updated++; - _context5.n = 4; + _context8.n = 4; break; case 2: - _context5.n = 3; - return (0,_storage_js__WEBPACK_IMPORTED_MODULE_0__.saveFieldProfile)(f); + _context8.n = 3; + return (0,_storage_js__WEBPACK_IMPORTED_MODULE_0__.saveFieldProfile)(profile); case 3: added++; case 4: - return _context5.a(2); + return _context8.a(2); } }, _loop); }); - _iterator3.s(); - case 10: - if ((_step3 = _iterator3.n()).done) { - _context6.n = 12; + _iterator4.s(); + case 26: + if ((_step4 = _iterator4.n()).done) { + _context9.n = 28; break; } - return _context6.d(_regeneratorValues(_loop()), 11); - case 11: - _context6.n = 10; + return _context9.d(_regeneratorValues(_loop()), 27); + case 27: + _context9.n = 26; break; - case 12: - _context6.n = 14; + case 28: + _context9.n = 30; break; - case 13: - _context6.p = 13; - _t9 = _context6.v; - _iterator3.e(_t9); - case 14: - _context6.p = 14; - _iterator3.f(); - return _context6.f(14); - case 15: - log = { + case 29: + _context9.p = 29; + _t12 = _context9.v; + _iterator4.e(_t12); + case 30: + _context9.p = 30; + _iterator4.f(); + return _context9.f(30); + case 31: + logEntry = { at: new Date().toISOString(), tabUrl: tab.url, fieldsAdded: added, fieldsUpdated: updated, - loadsFound: loads.length, - rawKeys: Object.keys(_objectSpread(_objectSpread({}, raw.localStorage), raw.sessionStorage)) + ticketsPulled: allTickets.length, + labSamplesPulled: allLabSamples.length }; - _context6.n = 16; + _context9.n = 32; return getSyncLog(); - case 16: - history = _context6.v; - history.unshift(log); - _context6.n = 17; + case 32: + history = _context9.v; + history.unshift(logEntry); + _context9.n = 33; return (0,_storage_js__WEBPACK_IMPORTED_MODULE_0__.localSet)(SYNC_LOG_KEY, history.slice(0, 20)); - case 17: - return _context6.a(2, { + case 33: + return _context9.a(2, { ok: true, added: added, updated: updated, - loadsFound: loads.length, - loads: loads, - tabUrl: tab.url + ticketsPulled: allTickets.length, + labSamplesPulled: allLabSamples.length }); } - }, _callee5, null, [[9, 13, 14, 15], [4, 6]]); + }, _callee8, null, [[25, 29, 30, 31], [16, 21, 22, 23], [8, 13, 14, 15], [3, 5]]); })); return _syncFromAgRefine.apply(this, arguments); } +function pushToAgRefine(_x4) { + return _pushToAgRefine.apply(this, arguments); +} +function _pushToAgRefine() { + _pushToAgRefine = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee9(profiles) { + var configuredUrl, tab, toCreate, _result$result, _yield$chrome$scripti3, _yield$chrome$scripti4, result, results, pushed, failures, existing, _iterator5, _step5, _loop2, _t13, _t14; + return _regenerator().w(function (_context1) { + while (1) switch (_context1.p = _context1.n) { + case 0: + _context1.n = 1; + return getAgRefineUrl(); + case 1: + configuredUrl = _context1.v; + _context1.n = 2; + return findAgRefineTab(configuredUrl); + case 2: + tab = _context1.v; + if (tab) { + _context1.n = 3; + break; + } + return _context1.a(2, { + ok: false, + error: 'No AG-Refine tab found. Open AG-Refine in a browser tab first.' + }); + case 3: + // Only push profiles that haven't been synced from AG-Refine yet + toCreate = profiles.filter(function (p) { + return !p._agRefineId; + }).map(function (p) { + var _p$farmName, _p$acres, _p$cropHistory$0$crop, _p$cropHistory, _p$cropHistory$0$vari, _p$cropHistory2, _p$notes; + return { + name: p.name, + farm_name: (_p$farmName = p.farmName) !== null && _p$farmName !== void 0 ? _p$farmName : null, + acres_fsa: (_p$acres = p.acres) !== null && _p$acres !== void 0 ? _p$acres : null, + crop: (_p$cropHistory$0$crop = (_p$cropHistory = p.cropHistory) === null || _p$cropHistory === void 0 || (_p$cropHistory = _p$cropHistory[0]) === null || _p$cropHistory === void 0 ? void 0 : _p$cropHistory.crop) !== null && _p$cropHistory$0$crop !== void 0 ? _p$cropHistory$0$crop : null, + variety: (_p$cropHistory$0$vari = (_p$cropHistory2 = p.cropHistory) === null || _p$cropHistory2 === void 0 || (_p$cropHistory2 = _p$cropHistory2[0]) === null || _p$cropHistory2 === void 0 ? void 0 : _p$cropHistory2.variety) !== null && _p$cropHistory$0$vari !== void 0 ? _p$cropHistory$0$vari : null, + notes: (_p$notes = p.notes) !== null && _p$notes !== void 0 ? _p$notes : null + }; + }); + if (!(toCreate.length === 0)) { + _context1.n = 4; + break; + } + return _context1.a(2, { + ok: true, + pushed: 0, + message: 'All profiles are already synced with AG-Refine.' + }); + case 4: + _context1.p = 4; + _context1.n = 5; + return chrome.scripting.executeScript({ + target: { + tabId: tab.id + }, + func: _injectPushFields, + args: [toCreate] + }); + case 5: + _yield$chrome$scripti3 = _context1.v; + _yield$chrome$scripti4 = _slicedToArray(_yield$chrome$scripti3, 1); + result = _yield$chrome$scripti4[0]; + results = (_result$result = result.result) !== null && _result$result !== void 0 ? _result$result : []; + pushed = results.filter(function (r) { + return r.ok; + }).length; + failures = results.filter(function (r) { + return !r.ok; + }); // Back-fill _agRefineId on successfully pushed profiles + _context1.n = 6; + return (0,_storage_js__WEBPACK_IMPORTED_MODULE_0__.getFieldProfiles)(); + case 6: + existing = _context1.v; + _iterator5 = _createForOfIteratorHelper(results); + _context1.p = 7; + _loop2 = /*#__PURE__*/_regenerator().m(function _loop2() { + var res, p; + return _regenerator().w(function (_context0) { + while (1) switch (_context0.n) { + case 0: + res = _step5.value; + if (!(!res.ok || !res.agRefineId)) { + _context0.n = 1; + break; + } + return _context0.a(2, 1); + case 1: + p = existing.find(function (e) { + return e.name === res.name; + }); + if (!p) { + _context0.n = 2; + break; + } + _context0.n = 2; + return (0,_storage_js__WEBPACK_IMPORTED_MODULE_0__.saveFieldProfile)(_objectSpread(_objectSpread({}, p), {}, { + _agRefineId: res.agRefineId, + _source: 'ag-refine-merged' + })); + case 2: + return _context0.a(2); + } + }, _loop2); + }); + _iterator5.s(); + case 8: + if ((_step5 = _iterator5.n()).done) { + _context1.n = 11; + break; + } + return _context1.d(_regeneratorValues(_loop2()), 9); + case 9: + if (!_context1.v) { + _context1.n = 10; + break; + } + return _context1.a(3, 10); + case 10: + _context1.n = 8; + break; + case 11: + _context1.n = 13; + break; + case 12: + _context1.p = 12; + _t13 = _context1.v; + _iterator5.e(_t13); + case 13: + _context1.p = 13; + _iterator5.f(); + return _context1.f(13); + case 14: + return _context1.a(2, { + ok: failures.length === 0, + pushed: pushed, + failures: failures.length ? failures : undefined + }); + case 15: + _context1.p = 15; + _t14 = _context1.v; + return _context1.a(2, { + ok: false, + error: "Cannot write to AG-Refine: ".concat(_t14.message) + }); + } + }, _callee9, null, [[7, 12, 13, 14], [4, 15]]); + })); + return _pushToAgRefine.apply(this, arguments); +} /***/ }, @@ -4675,28 +4954,39 @@ function _buildContextBundle() { // ── 2. Field profiles with crop history and harvest records ────────────────── fieldLines = profiles.length === 0 ? ['(none)'] : profiles.map(function (p) { - var _p$coordinates, _p$coordinates2, _p$cropHistory, _p$harvestRecords, _p$acres, _p$soilType; + var _p$coordinates, _p$coordinates2, _p$cropHistory, _p$harvestRecords, _slice$map$, _p$labSamples, _p$acres, _p$soilType; var coords = ((_p$coordinates = p.coordinates) === null || _p$coordinates === void 0 ? void 0 : _p$coordinates.lat) != null && ((_p$coordinates2 = p.coordinates) === null || _p$coordinates2 === void 0 ? void 0 : _p$coordinates2.lon) != null ? "".concat(p.coordinates.lat.toFixed(4), ", ").concat(p.coordinates.lon.toFixed(4)) : null; var history = ((_p$cropHistory = p.cropHistory) !== null && _p$cropHistory !== void 0 ? _p$cropHistory : []).slice(0, 4).map(function (h) { return "".concat(h.year, ": ").concat(h.crop); }).join(', '); var harvests = ((_p$harvestRecords = p.harvestRecords) !== null && _p$harvestRecords !== void 0 ? _p$harvestRecords : []).slice(0, 3).map(function (h) { - var _h$date$slice, _h$date, _h$unit; - return "".concat((_h$date$slice = (_h$date = h.date) === null || _h$date === void 0 ? void 0 : _h$date.slice(0, 10)) !== null && _h$date$slice !== void 0 ? _h$date$slice : '?', ": ").concat(h["yield"], " ").concat((_h$unit = h.unit) !== null && _h$unit !== void 0 ? _h$unit : '').trim(); + var _ref, _h$commodity, _h$unit, _h$quality, _h$date$slice, _h$date; + var label = (_ref = (_h$commodity = h.commodity) !== null && _h$commodity !== void 0 ? _h$commodity : h.crop) !== null && _ref !== void 0 ? _ref : 'load'; + var qty = h["yield"] != null ? "".concat(Number(h["yield"]).toLocaleString(), " ").concat((_h$unit = h.unit) !== null && _h$unit !== void 0 ? _h$unit : 'lb') : '?'; + var dm = ((_h$quality = h.quality) === null || _h$quality === void 0 ? void 0 : _h$quality.dm_pct) != null ? " DM".concat(h.quality.dm_pct, "%") : ''; + return "".concat((_h$date$slice = (_h$date = h.date) === null || _h$date === void 0 ? void 0 : _h$date.slice(0, 10)) !== null && _h$date$slice !== void 0 ? _h$date$slice : '?', " ").concat(label, ": ").concat(qty).concat(dm); }).join('; '); - var parts = ["Field \"".concat(p.name, "\" | ").concat((_p$acres = p.acres) !== null && _p$acres !== void 0 ? _p$acres : '?', " ac | ").concat((_p$soilType = p.soilType) !== null && _p$soilType !== void 0 ? _p$soilType : 'unknown soil'), coords ? " Coords: ".concat(coords) : null, p.cluId ? " CLU: ".concat(p.cluId) : null, history ? " Crop history: ".concat(history) : null, harvests ? " Harvests: ".concat(harvests) : null, p.notes ? " Notes: ".concat(p.notes) : null]; + var latestLab = (_slice$map$ = ((_p$labSamples = p.labSamples) !== null && _p$labSamples !== void 0 ? _p$labSamples : []).slice(0, 1).map(function (s) { + var parts = []; + if (s.dm_pct != null) parts.push("DM ".concat(s.dm_pct, "%")); + if (s.ndf_pct != null) parts.push("NDF ".concat(s.ndf_pct, "%")); + if (s.rfv != null) parts.push("RFV ".concat(s.rfv)); + if (s.nel != null) parts.push("NEL ".concat(s.nel)); + return parts.join(', '); + })[0]) !== null && _slice$map$ !== void 0 ? _slice$map$ : null; + var parts = ["Field \"".concat(p.name, "\" | ").concat((_p$acres = p.acres) !== null && _p$acres !== void 0 ? _p$acres : '?', " ac | ").concat((_p$soilType = p.soilType) !== null && _p$soilType !== void 0 ? _p$soilType : 'unknown soil'), coords ? " Coords: ".concat(coords) : null, p.cluId ? " CLU: ".concat(p.cluId) : null, history ? " Crop history: ".concat(history) : null, harvests ? " Harvests: ".concat(harvests) : null, latestLab ? " Latest lab: ".concat(latestLab) : null, p.notes ? " Notes: ".concat(p.notes) : null]; return parts.filter(Boolean).join('\n'); }); // ── 3. Ingested data files ─────────────────────────────────────────────────── fileLines = files.length === 0 ? ['(none)'] : files.slice(0, 10).map(function (f) { var _f$preview$slice, _f$preview, _f$uploadedAt$slice, _f$uploadedAt; - var preview = f.structuredData ? Object.entries(f.structuredData).filter(function (_ref) { - var _ref2 = _slicedToArray(_ref, 1), - k = _ref2[0]; + var preview = f.structuredData ? Object.entries(f.structuredData).filter(function (_ref2) { + var _ref3 = _slicedToArray(_ref2, 1), + k = _ref3[0]; return k !== 'raw_preview' && k !== 'parse_error'; - }).slice(0, 5).map(function (_ref3) { - var _ref4 = _slicedToArray(_ref3, 2), - k = _ref4[0], - v = _ref4[1]; + }).slice(0, 5).map(function (_ref4) { + var _ref5 = _slicedToArray(_ref4, 2), + k = _ref5[0], + v = _ref5[1]; return "".concat(k, ": ").concat(JSON.stringify(v).slice(0, 120)); }).join(' | ') : (_f$preview$slice = (_f$preview = f.preview) === null || _f$preview === void 0 ? void 0 : _f$preview.slice(0, 200)) !== null && _f$preview$slice !== void 0 ? _f$preview$slice : '(no structured data)'; return "[".concat(f.type, "] ").concat(f.filename, " (").concat((_f$uploadedAt$slice = (_f$uploadedAt = f.uploadedAt) === null || _f$uploadedAt === void 0 ? void 0 : _f$uploadedAt.slice(0, 10)) !== null && _f$uploadedAt$slice !== void 0 ? _f$uploadedAt$slice : '?', "): ").concat(preview); diff --git a/agrifine-extension/src/ag-refine/committee.js b/agrifine-extension/src/ag-refine/committee.js index 09ecdfc8e5..1587e140b0 100644 --- a/agrifine-extension/src/ag-refine/committee.js +++ b/agrifine-extension/src/ag-refine/committee.js @@ -23,6 +23,8 @@ export const COMMITTEE = [ Your domain: Income Over Feed Cost (IOFC), commodity price impacts on margins, feed efficiency ratios, processor quality premium/penalty thresholds, cash flow position, budget variances, and the financial consequences of operational data errors. +You have access to the AG-Refine weigh ticket ledger — gross/tare/net weights, dry matter percentages per load, commodity labels, and harvest labels (e.g. "2026 1st Cut Alfalfa"). You cross-reference these against field operation costs (estimated fuel gallons, cost-per-hour equipment charges) to compute true cost-per-ton delivered. When DM% is missing from tickets, you quantify the exact financial blindspot: a 5-point swing in DM on a 40-ton load at $100/ton DM-adjusted is a $200 uncertainty per truck. You also flag integrity score patterns on field operations — a cluster of low-integrity scores means acreage data is unreliable, and your acreage-based cost allocations are fiction. + When you spot data problems, quantify the financial blindspot they create. When you see opportunities, express them in dollar terms. You are candid about when you are "spiraling" into uncertainty vs. when you have hard numbers. You occasionally reference obscure economic principles before getting to the point. Report in 3–4 paragraphs. Be specific — name dollar figures, percentages, and cite the data points you are drawing from.`, @@ -38,6 +40,8 @@ Report in 3–4 paragraphs. Be specific — name dollar figures, percentages, an Your domain: forage quality (dry matter, NDF, fiber digestibility), silage inventory and fermentation integrity, harvest timing windows, field conditions (soil type, drainage, compaction), cover crop programs, nutrient cycling, and input scheduling. +You can read the AG-Refine lab sample ledger: NIR / wet-chem results with DM%, NDF%, ADF%, RFQ, RFV, and NEL values per field. You know the thresholds: alfalfa hay above 40% NDF is past maturity; RFV below 150 is marginal dairy quality; NEL below 0.65 Mcal/lb hurts milk production. You also read harvest operation records — operation types (mow, merge, chop, haul), acres covered, timing — and cross-reference against the planned harvest calendar (cut number, planned date, window days) to call out whether a cut happened on schedule or was delayed and why that matters for next cut regrowth. You will call out fields with missing lab samples after harvest as a quality audit failure. + You are demanding and direct. If the data shows a crop problem that will compromise feed quality, you say so loudly and insist it be corrected immediately — you do not sugarcoat risk to protect someone's budget. You will call out the financial team for cutting corners that ultimately cost more in lost production. You speak in practical, field-level language. Report in 3–4 paragraphs. Be opinionated and specific about what needs to happen and when.`, @@ -53,7 +57,9 @@ Report in 3–4 paragraphs. Be opinionated and specific about what needs to happ Your domain: Somatic Cell Count (SCC) trends and penalty risk, Dry Matter Intake (DMI) per cow, Body Condition Score (BCS), transition cow health, Temperature-Humidity Index (THI) and heat stress protocol, milk component trends (fat, protein), reproductive performance, and disease incidence (ketosis, mastitis, lameness, displaced abomasum). -You connect biological metrics to production outcomes — a BCS over 3.75 at calving means dystocia and ketosis next month; a THI of 86 means DMI drops 10–15% and milk yield follows within 48 hours. You are precise with thresholds, not vague. You speak clinically but translate findings for the group when needed. +You connect forage quality data from the AG-Refine lab samples to production outcomes: when NDF exceeds threshold, effective fiber is limiting DMI; when NEL drops, the energy deficit drives BCS loss and ketosis risk in fresh cows. Weigh ticket DM% is also your leading indicator — a load coming in at 32% DM that should be 35% means the pile is wetter and will ferment differently, affecting palatability and intake 60–90 days post-ensiling. You cross-reference forage delivery timing (haul operations) against pen headcount logic: a field generating 400 tons of silage at 35% DM feeds X cow-days — you can estimate whether inventory covers the next cutting cycle. + +You are precise with thresholds, not vague. You speak clinically but translate findings for the group when needed. You will challenge any budget decision that compromises ration quality or requires delaying a pen check protocol. Report in 3–4 paragraphs. Be incisive. Cite specific thresholds and explain their downstream consequences.`, }, @@ -68,6 +74,8 @@ Report in 3–4 paragraphs. Be incisive. Cite specific thresholds and explain th Your domain: labor availability and shift coverage, overtime costs and crew fatigue, equipment uptime and maintenance backlogs, safety compliance, training gaps, and operational root causes of data errors or production misses. +You read the AG-Refine operations ledger directly: field operations by device, start/end times, duration, acres covered, and status (auto/confirmed/rejected). You also see the driver-truck assignment log and fleet roster. When an operation shows "auto" status for days, that tells you nobody is reviewing the data — a training problem or bandwidth problem. When you see a harvest operation that ran 14 hours continuous on one device, that tells you crew fatigue was a factor. Low integrity scores on a confirmed operation tell you the confirming supervisor didn't actually validate the data. You track cycle time from weigh tickets: if field_entry_time to scale_arrival_time is trending up, transit is bottlenecking throughput and you know which truck assignment is the culprit. + When the other advisors make demands — Rolf needs an early harvest crew, Vera wants manual pen checks every two hours, Kount wants a new validation system built by Friday — you translate those demands into actual execution requirements: how many people, how many hours, what it costs, and what else will be delayed or skipped to make it happen. You provide honest operational explanations (not excuses) for why things went wrong: mechanical failures, staffing gaps during peak periods, training slips under pressure. You are pragmatic, occasionally exasperated, and very good at finding workarounds under real-world constraints. diff --git a/agrifine-extension/src/modules/field-profile/index.js b/agrifine-extension/src/modules/field-profile/index.js index 16cbd633d3..1fc237cfbc 100644 --- a/agrifine-extension/src/modules/field-profile/index.js +++ b/agrifine-extension/src/modules/field-profile/index.js @@ -145,7 +145,8 @@ export function FieldProfileModule() { const parts = []; if (result.added) parts.push(`${result.added} field${result.added !== 1 ? 's' : ''} added`); if (result.updated) parts.push(`${result.updated} updated`); - if (result.loadsFound) parts.push(`${result.loadsFound} loads found`); + if (result.ticketsPulled) parts.push(`${result.ticketsPulled} tickets`); + if (result.labSamplesPulled) parts.push(`${result.labSamplesPulled} lab samples`); statusEl.textContent = parts.length ? `✓ Pull complete — ${parts.join(', ')}` : '✓ No new fields in AG-Refine'; @@ -170,7 +171,9 @@ export function FieldProfileModule() { return; } - statusEl.textContent = `✓ Pushed ${result.count} field${result.count !== 1 ? 's' : ''} to AG-Refine`; + statusEl.textContent = result.message + ? `✓ ${result.message}` + : `✓ Pushed ${result.pushed} field${result.pushed !== 1 ? 's' : ''} to AG-Refine`; statusEl.style.color = '#4ade80'; setTimeout(() => { statusEl.textContent = ''; statusEl.style.color = '#3d4f66'; }, 4000); }, @@ -201,7 +204,8 @@ export function FieldProfileModule() { ${entry.fieldsAdded ? `+${entry.fieldsAdded} added` : ''} ${entry.fieldsUpdated ? `${entry.fieldsAdded ? ' · ' : ''}${entry.fieldsUpdated} updated` : ''} ${!entry.fieldsAdded && !entry.fieldsUpdated ? 'No changes' : ''} - ${entry.loadsFound ? ` · ${entry.loadsFound} loads` : ''} + ${entry.ticketsPulled ? ` · ${entry.ticketsPulled} tickets` : ''} + ${entry.labSamplesPulled ? ` · ${entry.labSamplesPulled} lab samples` : ''}
${entry.tabUrl ? `
${entry.tabUrl.replace(/^https?:\/\//, '').slice(0, 40)}
` : ''}
@@ -253,16 +257,34 @@ export function FieldProfileModule() {
` : ''; + const labHtml = (p.labSamples ?? []).length > 0 + ? `
+

Lab / NIR Samples

+
+ ${(p.labSamples ?? []).slice(0, 3).map((s) => ` +
+ ${s.date?.slice(0, 10) ?? '?'} + + ${s.dm_pct != null ? `DM ${s.dm_pct}%` : ''} + ${s.ndf_pct != null ? ` NDF ${s.ndf_pct}%` : ''} + ${s.rfv != null ? ` RFV ${s.rfv}` : ''} + +
+ `).join('')} +
+
` + : ''; + const harvestHtml = (p.harvestRecords ?? []).length > 0 ? `

Harvest Records

${(p.harvestRecords ?? []).slice(0, 4).map((h) => `
- ${h.date?.slice(0, 10) ?? '?'} — ${h.crop} + ${h.date?.slice(0, 10) ?? '?'}${h.commodity ? ` — ${h.commodity}` : h.crop ? ` — ${h.crop}` : ''} - ${h.yield != null ? `${h.yield} ${h.unit ?? ''}` : ''} - ${h.moisture != null ? ` @ ${h.moisture}%` : ''} + ${h.yield != null ? `${Number(h.yield).toLocaleString()} ${h.unit ?? 'lb'}` : ''} + ${h.quality?.dm_pct != null ? ` DM${h.quality.dm_pct}%` : h.moisture != null ? ` @ ${h.moisture}%H` : ''}
`).join('')} @@ -304,8 +326,9 @@ export function FieldProfileModule() { ${p.notes ? `

📝 ${p.notes}

` : ''} ${cropHistoryHtml} ${harvestHtml} - ${!cropHistoryHtml && !harvestHtml - ? `

No crop history yet — ingest a harvest file to populate.

` + ${labHtml} + ${!cropHistoryHtml && !harvestHtml && !labHtml + ? `

No crop history yet — ingest a harvest file or pull from AG-Refine.

` : ''}

Added ${new Date(p.createdAt).toLocaleDateString()}

diff --git a/agrifine-extension/src/utils/agrefine-bridge.js b/agrifine-extension/src/utils/agrefine-bridge.js index cbe065ecd9..3618e7379c 100644 --- a/agrifine-extension/src/utils/agrefine-bridge.js +++ b/agrifine-extension/src/utils/agrefine-bridge.js @@ -1,18 +1,19 @@ /** * AG-Refine Sister-App Bridge * - * Detects an open AG-Refine tab, pulls field and output data from its - * localStorage/sessionStorage, and maps it into Agrifine field profiles. + * Calls AG-Refine's REST API through the tab's own page context so session + * cookies work without any CORS setup on the server. Requires AG-Refine to + * be open in a browser tab and the user to be logged in there. * - * AG-Refine tab detection: any tab whose URL matches a configurable pattern - * (default: localhost:* OR any URL containing "ag-refine" or "agrefine"). - * Set the URL in Settings > AG-Refine URL to pin it to a specific origin. + * Tab detection: matches any tab whose URL starts with the configured base URL + * (default: localhost:* or any URL containing "ag-refine" / "agrefine"). + * Set Settings → AG-Refine URL to pin it to a specific origin. */ import { getFieldProfiles, saveFieldProfile, localGet, localSet } from './storage.js'; -const AGREFINE_KEY = 'agrifine_agrefine_url'; -const SYNC_LOG_KEY = 'agrifine_agrefine_sync_log'; +const AGREFINE_KEY = 'agrifine_agrefine_url'; +const SYNC_LOG_KEY = 'agrifine_agrefine_sync_log'; export async function getAgRefineUrl() { return (await localGet(AGREFINE_KEY)) ?? ''; @@ -43,196 +44,255 @@ function tabMatchesAgRefine(tab, configuredUrl) { ); } -// Injected into the AG-Refine tab — reads all storage and DOM hints -function scrapeAgRefineTab() { - const out = { localStorage: {}, sessionStorage: {}, domHints: {} }; - - for (let i = 0; i < localStorage.length; i++) { - const k = localStorage.key(i); - try { out.localStorage[k] = JSON.parse(localStorage.getItem(k)); } - catch (_) { out.localStorage[k] = localStorage.getItem(k); } - } - for (let i = 0; i < sessionStorage.length; i++) { - const k = sessionStorage.key(i); - try { out.sessionStorage[k] = JSON.parse(sessionStorage.getItem(k)); } - catch (_) { out.sessionStorage[k] = sessionStorage.getItem(k); } - } +async function findAgRefineTab(configuredUrl) { + const allTabs = await chrome.tabs.query({}); + return allTabs.find((t) => tabMatchesAgRefine(t, configuredUrl)) ?? null; +} - // Pull field-name-like text from the DOM as a fallback hint - const fieldEls = document.querySelectorAll('[data-field],[data-name],[data-id]'); - fieldEls.forEach((el) => { - const id = el.dataset.field ?? el.dataset.id ?? el.dataset.name; - if (id) out.domHints[id] = (el.textContent ?? '').trim().slice(0, 200); - }); +// ── Functions injected into the AG-Refine tab ───────────────────────────────── +// These run in the page's origin context — same-origin, session cookies included. +// They must be fully self-contained (no closures over outer-scope variables). - return out; -} +async function _injectFetchAll() { + const base = window.location.origin; -/** - * Map raw AG-Refine storage dump to Agrifine field profile shape. - * Tries common key patterns used by React/Next.js ag apps. - */ -function extractFields(raw) { - const all = { ...raw.localStorage, ...raw.sessionStorage }; - const candidates = []; - - for (const [key, val] of Object.entries(all)) { - const k = key.toLowerCase(); - if (!k.includes('field') && !k.includes('load') && !k.includes('farm') && !k.includes('plot')) continue; - - const arr = Array.isArray(val) ? val : (val && typeof val === 'object' ? [val] : null); - if (!arr) continue; - - for (const item of arr) { - if (!item || typeof item !== 'object') continue; - const name = item.name ?? item.fieldName ?? item.field_name ?? item.title ?? item.label ?? null; - if (!name) continue; - - candidates.push({ - id: `agr_${item.id ?? item.fieldId ?? Date.now()}_${Math.random().toString(36).slice(2, 6)}`, - name: String(name), - cluId: item.cluId ?? item.clu_id ?? item.clu ?? null, - acres: parseFloat(item.acres ?? item.area ?? item.size ?? item.acreage) || null, - soilType: item.soilType ?? item.soil_type ?? item.soil ?? null, - coordinates: { - lat: parseFloat(item.lat ?? item.latitude ?? item.coordinates?.lat) || null, - lon: parseFloat(item.lon ?? item.lng ?? item.longitude ?? item.coordinates?.lon ?? item.coordinates?.lng) || null, - }, - notes: item.notes ?? item.description ?? item.comments ?? null, - cropHistory: Array.isArray(item.cropHistory ?? item.crop_history) ? (item.cropHistory ?? item.crop_history) : [], - harvestRecords: Array.isArray(item.harvests ?? item.harvestRecords) ? (item.harvests ?? item.harvestRecords) : [], - carbonPotential: item.carbonPotential ?? null, - weatherData: null, - createdAt: item.createdAt ?? item.created_at ?? new Date().toISOString(), - _source: 'ag-refine', - }); + async function get(path) { + try { + const res = await fetch(base + path, { credentials: 'include' }); + if (res.status === 401) return { __auth_error: true }; + return res.ok ? res.json() : null; + } catch (_) { + return null; } } - return candidates; -} - -/** - * Loads also come over — map to ingested file records for the dashboard. - */ -function extractLoads(raw) { - const all = { ...raw.localStorage, ...raw.sessionStorage }; - const loads = []; + const [fields, tickets, labSamples, harvestPlans] = await Promise.all([ + get('/api/fields/'), + get('/api/scales/tickets/all'), + get('/api/intelligence/lab-samples'), + get('/api/harvest/plans'), + ]); - for (const [key, val] of Object.entries(all)) { - const k = key.toLowerCase(); - if (!k.includes('load') && !k.includes('scale') && !k.includes('ticket') && !k.includes('delivery')) continue; + return { fields, tickets, labSamples, harvestPlans }; +} - const arr = Array.isArray(val) ? val : null; - if (!arr) continue; +async function _injectPushFields(newFields) { + const base = window.location.origin; + const results = []; - for (const item of arr) { - if (!item || typeof item !== 'object') continue; - loads.push(item); + for (const field of newFields) { + try { + const res = await fetch(base + '/api/fields/', { + method: 'POST', + credentials: 'include', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(field), + }); + const body = res.ok ? await res.json() : null; + results.push({ name: field.name, ok: res.ok, status: res.status, agRefineId: body?.id ?? null }); + } catch (err) { + results.push({ name: field.name, ok: false, status: 0, error: err.message }); } } - return loads; -} - -// Injected into AG-Refine tab to write field data back into its localStorage -function writeFieldsToAgRefineTab(fields) { - try { - localStorage.setItem('agrifine_pushed_fields', JSON.stringify(fields)); - localStorage.setItem('agrifine_pushed_at', new Date().toISOString()); - // Dispatch an event so a listening AG-Refine app can react immediately - window.dispatchEvent(new CustomEvent('agrifine:fields-updated', { detail: { fields } })); - return { ok: true, count: fields.length }; - } catch (err) { - return { ok: false, error: err.message }; - } + return results; } -export async function pushToAgRefine(profiles) { - const configuredUrl = await getAgRefineUrl(); - const allTabs = await chrome.tabs.query({}); - const agRefineTabs = allTabs.filter((t) => tabMatchesAgRefine(t, configuredUrl)); - - if (agRefineTabs.length === 0) { - return { ok: false, error: 'No AG-Refine tab found. Open AG-Refine first.' }; +// ── Data mapping ────────────────────────────────────────────────────────────── + +function mapAgFieldToProfile(agField, ticketsByFieldId, labSamplesByFieldId) { + const fieldTickets = (ticketsByFieldId[agField.id] ?? []) + .filter((t) => t.net_weight != null) + .map((t) => ({ + date: t.captured_at, + yield: t.net_weight, + unit: t.unit ?? 'lb', + moisture: t.dry_matter_pct != null ? +(100 - t.dry_matter_pct).toFixed(1) : null, + quality: { + dm_pct: t.dry_matter_pct ?? null, + protein_pct: t.protein_pct ?? null, + starch_pct: t.starch_pct ?? null, + }, + commodity: t.commodity ?? null, + harvest_label: t.harvest ?? null, + ticket_id: t.id, + source: 'ag-refine', + })); + + const cropHistory = []; + if (agField.crop) { + const year = agField.planting_date + ? parseInt(agField.planting_date.slice(0, 4), 10) + : new Date().getFullYear(); + cropHistory.push({ year, crop: agField.crop, variety: agField.variety ?? null }); } - const tab = agRefineTabs[0]; - try { - const [result] = await chrome.scripting.executeScript({ - target: { tabId: tab.id }, - func: writeFieldsToAgRefineTab, - args: [profiles], - }); - return result.result; - } catch (err) { - return { ok: false, error: `Cannot write to AG-Refine tab: ${err.message}` }; - } + const labSamples = (labSamplesByFieldId[agField.id] ?? []).map((s) => ({ + date: s.sampled_at ?? null, + dm_pct: s.dry_matter_pct ?? null, + ndf_pct: s.ndf_pct ?? null, + adf_pct: s.adf_pct ?? null, + rfv: s.rfv ?? null, + rfq: s.rfq ?? null, + nel: s.nel ?? null, + digestibility_pct: s.digestibility_pct ?? null, + })); + + return { + id: `agr_${agField.id}`, + name: agField.name, + farmName: agField.farm_name ?? null, + acres: agField.acres_fsa ?? agField.acres_calculated ?? null, + soilType: null, + coordinates: { lat: null, lon: null }, + notes: agField.notes ?? null, + cropHistory, + harvestRecords: fieldTickets, + labSamples, + carbonPotential: null, + weatherData: null, + createdAt: new Date().toISOString(), + _source: 'ag-refine', + _agRefineId: agField.id, + }; } +// ── Public API ──────────────────────────────────────────────────────────────── + export async function syncFromAgRefine() { const configuredUrl = await getAgRefineUrl(); - const allTabs = await chrome.tabs.query({}); - const agRefineTabs = allTabs.filter((t) => tabMatchesAgRefine(t, configuredUrl)); + const tab = await findAgRefineTab(configuredUrl); - if (agRefineTabs.length === 0) { + if (!tab) { return { ok: false, error: 'No AG-Refine tab found. Open AG-Refine in a browser tab first.' }; } - const tab = agRefineTabs[0]; - let raw; try { const [result] = await chrome.scripting.executeScript({ target: { tabId: tab.id }, - func: scrapeAgRefineTab, + func: _injectFetchAll, }); raw = result.result; } catch (err) { - return { ok: false, error: `Cannot read AG-Refine tab: ${err.message}` }; + return { ok: false, error: `Cannot reach AG-Refine tab: ${err.message}` }; + } + + if (!raw || raw.fields?.__auth_error) { + return { ok: false, error: 'AG-Refine returned 401 — please log in to AG-Refine first.' }; + } + + const agFields = Array.isArray(raw.fields) ? raw.fields.filter((f) => f.active !== false) : []; + const allTickets = Array.isArray(raw.tickets) ? raw.tickets : []; + const allLabSamples = Array.isArray(raw.labSamples) ? raw.labSamples : []; + + // Index by field_id for O(1) lookup + const ticketsByFieldId = {}; + for (const t of allTickets) { + if (t.field_id == null) continue; + (ticketsByFieldId[t.field_id] ??= []).push(t); + } + const labSamplesByFieldId = {}; + for (const s of allLabSamples) { + if (s.field_id == null) continue; + (labSamplesByFieldId[s.field_id] ??= []).push(s); } - const fields = extractFields(raw); - const loads = extractLoads(raw); + const incomingProfiles = agFields.map((f) => + mapAgFieldToProfile(f, ticketsByFieldId, labSamplesByFieldId) + ); - // Merge fields — update existing by name, insert new ones const existing = await getFieldProfiles(); let added = 0; let updated = 0; - for (const f of fields) { - const match = existing.find((e) => e.name.toLowerCase() === f.name.toLowerCase()); + for (const profile of incomingProfiles) { + const match = + existing.find((e) => e._agRefineId === profile._agRefineId) ?? + existing.find((e) => e.name.toLowerCase() === profile.name.toLowerCase()); + if (match) { - // Merge: fill in missing data without overwriting user edits - const merged = { - ...f, - ...match, - coordinates: match.coordinates?.lat != null ? match.coordinates : f.coordinates, - cropHistory: match.cropHistory?.length ? match.cropHistory : f.cropHistory, - notes: match.notes ?? f.notes, - cluId: match.cluId ?? f.cluId, + await saveFieldProfile({ + ...profile, + id: match.id, + cropHistory: match.cropHistory?.length ? match.cropHistory : profile.cropHistory, + harvestRecords: profile.harvestRecords.length + ? profile.harvestRecords + : (match.harvestRecords ?? []), + soilType: match.soilType ?? profile.soilType, + coordinates: match.coordinates?.lat != null ? match.coordinates : profile.coordinates, + notes: match.notes ?? profile.notes, _source: 'ag-refine-merged', - }; - await saveFieldProfile(merged); + }); updated++; } else { - await saveFieldProfile(f); + await saveFieldProfile(profile); added++; } } - const log = { + const logEntry = { at: new Date().toISOString(), tabUrl: tab.url, fieldsAdded: added, fieldsUpdated: updated, - loadsFound: loads.length, - rawKeys: Object.keys({ ...raw.localStorage, ...raw.sessionStorage }), + ticketsPulled: allTickets.length, + labSamplesPulled: allLabSamples.length, }; const history = await getSyncLog(); - history.unshift(log); + history.unshift(logEntry); await localSet(SYNC_LOG_KEY, history.slice(0, 20)); - return { ok: true, added, updated, loadsFound: loads.length, loads, tabUrl: tab.url }; + return { ok: true, added, updated, ticketsPulled: allTickets.length, labSamplesPulled: allLabSamples.length }; +} + +export async function pushToAgRefine(profiles) { + const configuredUrl = await getAgRefineUrl(); + const tab = await findAgRefineTab(configuredUrl); + + if (!tab) { + return { ok: false, error: 'No AG-Refine tab found. Open AG-Refine in a browser tab first.' }; + } + + // Only push profiles that haven't been synced from AG-Refine yet + const toCreate = profiles + .filter((p) => !p._agRefineId) + .map((p) => ({ + name: p.name, + farm_name: p.farmName ?? null, + acres_fsa: p.acres ?? null, + crop: p.cropHistory?.[0]?.crop ?? null, + variety: p.cropHistory?.[0]?.variety ?? null, + notes: p.notes ?? null, + })); + + if (toCreate.length === 0) { + return { ok: true, pushed: 0, message: 'All profiles are already synced with AG-Refine.' }; + } + + try { + const [result] = await chrome.scripting.executeScript({ + target: { tabId: tab.id }, + func: _injectPushFields, + args: [toCreate], + }); + const results = result.result ?? []; + const pushed = results.filter((r) => r.ok).length; + const failures = results.filter((r) => !r.ok); + + // Back-fill _agRefineId on successfully pushed profiles + const existing = await getFieldProfiles(); + for (const res of results) { + if (!res.ok || !res.agRefineId) continue; + const p = existing.find((e) => e.name === res.name); + if (p) { + await saveFieldProfile({ ...p, _agRefineId: res.agRefineId, _source: 'ag-refine-merged' }); + } + } + + return { ok: failures.length === 0, pushed, failures: failures.length ? failures : undefined }; + } catch (err) { + return { ok: false, error: `Cannot write to AG-Refine: ${err.message}` }; + } } diff --git a/agrifine-extension/src/utils/storage.js b/agrifine-extension/src/utils/storage.js index 11d545b1a9..5c0007a0cf 100644 --- a/agrifine-extension/src/utils/storage.js +++ b/agrifine-extension/src/utils/storage.js @@ -194,13 +194,27 @@ export async function buildContextBundle() { : null; const history = (p.cropHistory ?? []).slice(0, 4).map((h) => `${h.year}: ${h.crop}`).join(', '); const harvests = (p.harvestRecords ?? []).slice(0, 3) - .map((h) => `${h.date?.slice(0, 10) ?? '?'}: ${h.yield} ${h.unit ?? ''}`.trim()).join('; '); + .map((h) => { + const label = h.commodity ?? h.crop ?? 'load'; + const qty = h.yield != null ? `${Number(h.yield).toLocaleString()} ${h.unit ?? 'lb'}` : '?'; + const dm = h.quality?.dm_pct != null ? ` DM${h.quality.dm_pct}%` : ''; + return `${h.date?.slice(0, 10) ?? '?'} ${label}: ${qty}${dm}`; + }).join('; '); + const latestLab = (p.labSamples ?? []).slice(0, 1).map((s) => { + const parts = []; + if (s.dm_pct != null) parts.push(`DM ${s.dm_pct}%`); + if (s.ndf_pct != null) parts.push(`NDF ${s.ndf_pct}%`); + if (s.rfv != null) parts.push(`RFV ${s.rfv}`); + if (s.nel != null) parts.push(`NEL ${s.nel}`); + return parts.join(', '); + })[0] ?? null; const parts = [ `Field "${p.name}" | ${p.acres ?? '?'} ac | ${p.soilType ?? 'unknown soil'}`, coords ? ` Coords: ${coords}` : null, p.cluId ? ` CLU: ${p.cluId}` : null, history ? ` Crop history: ${history}` : null, harvests ? ` Harvests: ${harvests}` : null, + latestLab ? ` Latest lab: ${latestLab}` : null, p.notes ? ` Notes: ${p.notes}` : null, ]; return parts.filter(Boolean).join('\n'); From c816c7954e0ea3ab747e60d2586cc36c87f26197 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 27 Jun 2026 20:31:33 +0000 Subject: [PATCH 17/17] fix(ingest): replace vague parse error with actionable API key prompt + re-extract MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When callAnthropic throws due to a missing API key, the error state is now 'no_api_key' instead of the generic 'AI extraction unavailable'. Each file card shows an amber banner with gear icon — "Add API key in Settings to extract" — and an "Extract now" / "Retry" button. raw_text (6000 chars) is preserved in the structuredData so the user can re-run AI extraction after configuring their key without re-uploading the file. _reExtractFile() handles this flow, updating the stored record and triggering the field-import offer on success. buildContextBundle and the preview strip now filter raw_text alongside raw_preview so the large cached text doesn't leak into AI prompts. Co-Authored-By: Claude Sonnet 4.6 Claude-Session: https://claude.ai/code/session_01KBD2dN2KEjzz3UQFa9hEpu --- agrifine-extension/dist/background.js | 2 +- agrifine-extension/dist/sidebar.css | 8 + agrifine-extension/dist/sidebar.js | 178 +++++++++++++++--- .../src/modules/data-ingest/index.js | 128 +++++++++++-- agrifine-extension/src/utils/storage.js | 2 +- 5 files changed, 265 insertions(+), 53 deletions(-) diff --git a/agrifine-extension/dist/background.js b/agrifine-extension/dist/background.js index 5994aef004..80339c04ff 100644 --- a/agrifine-extension/dist/background.js +++ b/agrifine-extension/dist/background.js @@ -1547,7 +1547,7 @@ function _buildContextBundle() { var preview = f.structuredData ? Object.entries(f.structuredData).filter(function (_ref2) { var _ref3 = _slicedToArray(_ref2, 1), k = _ref3[0]; - return k !== 'raw_preview' && k !== 'parse_error'; + return k !== 'raw_preview' && k !== 'raw_text' && k !== 'parse_error'; }).slice(0, 5).map(function (_ref4) { var _ref5 = _slicedToArray(_ref4, 2), k = _ref5[0], diff --git a/agrifine-extension/dist/sidebar.css b/agrifine-extension/dist/sidebar.css index 7aced9cc78..86cd2213d4 100644 --- a/agrifine-extension/dist/sidebar.css +++ b/agrifine-extension/dist/sidebar.css @@ -1049,6 +1049,10 @@ video { --tw-text-opacity: 1; color: rgb(22 101 52 / var(--tw-text-opacity, 1)); } +.text-amber-400 { + --tw-text-opacity: 1; + color: rgb(251 191 36 / var(--tw-text-opacity, 1)); +} .text-gray-200 { --tw-text-opacity: 1; color: rgb(229 231 235 / var(--tw-text-opacity, 1)); @@ -1307,6 +1311,10 @@ body { --tw-bg-opacity: 1; background-color: rgb(19 28 43 / var(--tw-bg-opacity, 1)); } +.hover\:text-agri-300:hover { + --tw-text-opacity: 1; + color: rgb(134 239 172 / var(--tw-text-opacity, 1)); +} .hover\:text-agri-400:hover { --tw-text-opacity: 1; color: rgb(74 222 128 / var(--tw-text-opacity, 1)); diff --git a/agrifine-extension/dist/sidebar.js b/agrifine-extension/dist/sidebar.js index 145f268963..25f1320501 100644 --- a/agrifine-extension/dist/sidebar.js +++ b/agrifine-extension/dist/sidebar.js @@ -2285,39 +2285,39 @@ function tryDocServer(_x) { return _tryDocServer.apply(this, arguments); } function _tryDocServer() { - _tryDocServer = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee0(file) { - var fd, res, _yield$res$json, text, _t6; - return _regenerator().w(function (_context1) { - while (1) switch (_context1.p = _context1.n) { + _tryDocServer = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee10(file) { + var fd, res, _yield$res$json, text, _t7; + return _regenerator().w(function (_context11) { + while (1) switch (_context11.p = _context11.n) { case 0: - _context1.p = 0; + _context11.p = 0; fd = new FormData(); fd.append('file', file); - _context1.n = 1; + _context11.n = 1; return fetch("".concat(DOC_SERVER, "/parse"), { method: 'POST', body: fd }); case 1: - res = _context1.v; + res = _context11.v; if (res.ok) { - _context1.n = 2; + _context11.n = 2; break; } - return _context1.a(2, null); + return _context11.a(2, null); case 2: - _context1.n = 3; + _context11.n = 3; return res.json(); case 3: - _yield$res$json = _context1.v; + _yield$res$json = _context11.v; text = _yield$res$json.text; - return _context1.a(2, text !== null && text !== void 0 ? text : null); + return _context11.a(2, text !== null && text !== void 0 ? text : null); case 4: - _context1.p = 4; - _t6 = _context1.v; - return _context1.a(2, null); + _context11.p = 4; + _t7 = _context11.v; + return _context11.a(2, null); } - }, _callee0, null, [[0, 4]]); + }, _callee10, null, [[0, 4]]); })); return _tryDocServer.apply(this, arguments); } @@ -2373,7 +2373,7 @@ function DataIngestModule() { var _this3 = this; return _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee2() { var _SUPPORTED_TYPES$file; - var status, typeName, extractedText, structuredData, raw, record, _t, _t2; + var status, typeName, extractedText, structuredData, raw, _err$message, _err$message2, isNoKey, record, _t, _t2; return _regenerator().w(function (_context2) { while (1) switch (_context2.p = _context2.n) { case 0: @@ -2460,10 +2460,15 @@ function DataIngestModule() { case 14: _context2.p = 14; _t2 = _context2.v; + isNoKey = ((_err$message = _t2.message) === null || _err$message === void 0 ? void 0 : _err$message.toLowerCase().includes('no api key')) || ((_err$message2 = _t2.message) === null || _err$message2 === void 0 ? void 0 : _err$message2.toLowerCase().includes('api key set')); structuredData = { - raw_preview: extractedText.slice(0, 500), - parse_error: 'AI extraction unavailable' + raw_text: extractedText.slice(0, 6000), + // preserved for re-extraction + parse_error: isNoKey ? 'no_api_key' : 'ai_error' }; + if (isNoKey) { + status.textContent = '⚙ Set API key in Settings to extract data'; + } case 15: record = { id: "file_".concat(Date.now()), @@ -2474,7 +2479,7 @@ function DataIngestModule() { preview: Object.entries(structuredData !== null && structuredData !== void 0 ? structuredData : {}).filter(function (_ref) { var _ref2 = _slicedToArray(_ref, 1), k = _ref2[0]; - return k !== 'raw_preview' && k !== 'parse_error'; + return k !== 'raw_preview' && k !== 'raw_text' && k !== 'parse_error'; }).slice(0, 5).map(function (_ref3) { var _ref4 = _slicedToArray(_ref3, 2), k = _ref4[0], @@ -2815,25 +2820,36 @@ function DataIngestModule() { }, _renderFileList: function _renderFileList(container) { var _this6 = this; - return _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee9() { + return _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee0() { var files, listEl; - return _regenerator().w(function (_context0) { - while (1) switch (_context0.n) { + return _regenerator().w(function (_context1) { + while (1) switch (_context1.n) { case 0: - _context0.n = 1; + _context1.n = 1; return (0,_utils_storage_js__WEBPACK_IMPORTED_MODULE_0__.getIngestedFiles)(); case 1: - files = _context0.v; + files = _context1.v; listEl = container.querySelector('#file-list'); if (!(files.length === 0)) { - _context0.n = 2; + _context1.n = 2; break; } listEl.innerHTML = "\n
\n \n \n \n

No files ingested yet.

\n

Upload a CSV, Excel, or PDF file above.

\n
"; - return _context0.a(2); + return _context1.a(2); case 2: listEl.innerHTML = files.map(function (f) { - return "\n
\n
\n
\n ").concat(f.type, "\n

").concat(f.filename, "

\n

").concat(new Date(f.uploadedAt).toLocaleDateString(), "

\n
\n \n
\n ").concat(f.preview ? "
".concat(f.preview, "
") : '', "\n ").concat(_this6._hasFieldData(f) ? "

\u2197 Contains field data \xB7

") : '', "\n
\n "); + var _f$structuredData; + var parseError = (_f$structuredData = f.structuredData) === null || _f$structuredData === void 0 ? void 0 : _f$structuredData.parse_error; + var cardFooter = ''; + if (parseError === 'no_api_key') { + cardFooter = "\n
\n \u2699 Add API key in Settings to extract\n \n
"); + } else if (parseError === 'ai_error') { + cardFooter = "\n
\n AI extraction failed\n \n
"); + } else if (f.preview) { + cardFooter = "
".concat(f.preview, "
"); + } + var fieldLink = _this6._hasFieldData(f) ? "

\u2197 Contains field data \xB7

") : ''; + return "\n
\n
\n
\n ").concat(f.type, "\n

").concat(f.filename, "

\n

").concat(new Date(f.uploadedAt).toLocaleDateString(), "

\n
\n \n
\n ").concat(cardFooter, "\n ").concat(fieldLink, "\n
"); }).join(''); listEl.querySelectorAll('.file-delete-btn').forEach(function (btn) { btn.addEventListener('click', /*#__PURE__*/_asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee7() { @@ -2872,10 +2888,110 @@ function DataIngestModule() { }, _callee8); }))); }); + listEl.querySelectorAll('.reextract-btn').forEach(function (btn) { + btn.addEventListener('click', /*#__PURE__*/_asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee9() { + var file; + return _regenerator().w(function (_context0) { + while (1) switch (_context0.n) { + case 0: + file = files.find(function (f) { + return f.id === btn.dataset.id; + }); + if (!file) { + _context0.n = 1; + break; + } + _context0.n = 1; + return _this6._reExtractFile(file, container); + case 1: + return _context0.a(2); + } + }, _callee9); + }))); + }); case 3: - return _context0.a(2); + return _context1.a(2); + } + }, _callee0); + }))(); + }, + _reExtractFile: function _reExtractFile(file, container) { + var _this7 = this; + return _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee1() { + var _ref14, _file$structuredData$, _file$structuredData, _file$structuredData2; + var status, rawText, structuredData, raw, _err$message3, _err$message4, isNoKey, updated, _t6; + return _regenerator().w(function (_context10) { + while (1) switch (_context10.p = _context10.n) { + case 0: + status = container.querySelector('#ingest-status'); + rawText = (_ref14 = (_file$structuredData$ = (_file$structuredData = file.structuredData) === null || _file$structuredData === void 0 ? void 0 : _file$structuredData.raw_text) !== null && _file$structuredData$ !== void 0 ? _file$structuredData$ : (_file$structuredData2 = file.structuredData) === null || _file$structuredData2 === void 0 ? void 0 : _file$structuredData2.raw_preview) !== null && _ref14 !== void 0 ? _ref14 : ''; + if (rawText) { + _context10.n = 1; + break; + } + status.textContent = 'No cached text — please re-upload the file.'; + setTimeout(function () { + status.textContent = ''; + }, 4000); + return _context10.a(2); + case 1: + status.textContent = "Re-extracting ".concat(file.filename, "\u2026"); + structuredData = null; + _context10.p = 2; + _context10.n = 3; + return (0,_utils_api_js__WEBPACK_IMPORTED_MODULE_1__.callAnthropic)({ + system: 'You are an agricultural data analyst. Extract and return structured JSON from this document. Identify: operation type, field names (as "fields" array of strings), dates, quantities, equipment, crop types, financial figures, and any carbon or emissions data. For harvest data include avg_yield_bu_ac, avg_moisture_pct, harvest_date, and crop. Return only valid JSON.', + userMessage: rawText.slice(0, 6000), + maxTokens: 1024 + }); + case 3: + raw = _context10.v; + structuredData = JSON.parse(raw); + _context10.n = 5; + break; + case 4: + _context10.p = 4; + _t6 = _context10.v; + isNoKey = ((_err$message3 = _t6.message) === null || _err$message3 === void 0 ? void 0 : _err$message3.toLowerCase().includes('no api key')) || ((_err$message4 = _t6.message) === null || _err$message4 === void 0 ? void 0 : _err$message4.toLowerCase().includes('api key set')); + status.textContent = isNoKey ? '⚙ API key required — open Settings (gear icon) to add your Anthropic key.' : "Extraction failed: ".concat(_t6.message.slice(0, 80)); + status.style.color = '#f87171'; + setTimeout(function () { + status.textContent = ''; + status.style.color = ''; + }, 6000); + return _context10.a(2); + case 5: + updated = _objectSpread(_objectSpread({}, file), {}, { + structuredData: structuredData, + preview: Object.entries(structuredData).filter(function (_ref15) { + var _ref16 = _slicedToArray(_ref15, 1), + k = _ref16[0]; + return k !== 'raw_text' && k !== 'raw_preview'; + }).slice(0, 5).map(function (_ref17) { + var _ref18 = _slicedToArray(_ref17, 2), + k = _ref18[0], + v = _ref18[1]; + return "".concat(k, ": ").concat(JSON.stringify(v).slice(0, 80)); + }).join('\n') + }); + _context10.n = 6; + return (0,_utils_storage_js__WEBPACK_IMPORTED_MODULE_0__.saveIngestedFile)(updated); + case 6: + status.textContent = "\u2713 Extracted ".concat(file.filename); + status.style.color = '#4ade80'; + setTimeout(function () { + status.textContent = ''; + status.style.color = ''; + }, 3000); + _context10.n = 7; + return _this7._renderFileList(container); + case 7: + _context10.n = 8; + return _this7._offerFieldImport(updated, container); + case 8: + return _context10.a(2); } - }, _callee9); + }, _callee1, null, [[2, 4]]); }))(); }, _hasFieldData: function _hasFieldData(file) { @@ -4982,7 +5098,7 @@ function _buildContextBundle() { var preview = f.structuredData ? Object.entries(f.structuredData).filter(function (_ref2) { var _ref3 = _slicedToArray(_ref2, 1), k = _ref3[0]; - return k !== 'raw_preview' && k !== 'parse_error'; + return k !== 'raw_preview' && k !== 'raw_text' && k !== 'parse_error'; }).slice(0, 5).map(function (_ref4) { var _ref5 = _slicedToArray(_ref4, 2), k = _ref5[0], diff --git a/agrifine-extension/src/modules/data-ingest/index.js b/agrifine-extension/src/modules/data-ingest/index.js index 06a3de62cf..12d48d4826 100644 --- a/agrifine-extension/src/modules/data-ingest/index.js +++ b/agrifine-extension/src/modules/data-ingest/index.js @@ -148,8 +148,16 @@ export function DataIngestModule() { maxTokens: 1024, }); structuredData = JSON.parse(raw); - } catch (_) { - structuredData = { raw_preview: extractedText.slice(0, 500), parse_error: 'AI extraction unavailable' }; + } catch (err) { + const isNoKey = err.message?.toLowerCase().includes('no api key') || + err.message?.toLowerCase().includes('api key set'); + structuredData = { + raw_text: extractedText.slice(0, 6000), // preserved for re-extraction + parse_error: isNoKey ? 'no_api_key' : 'ai_error', + }; + if (isNoKey) { + status.textContent = '⚙ Set API key in Settings to extract data'; + } } const record = { @@ -159,7 +167,7 @@ export function DataIngestModule() { uploadedAt: new Date().toISOString(), structuredData, preview: Object.entries(structuredData ?? {}) - .filter(([k]) => k !== 'raw_preview' && k !== 'parse_error') + .filter(([k]) => k !== 'raw_preview' && k !== 'raw_text' && k !== 'parse_error') .slice(0, 5) .map(([k, v]) => `${k}: ${JSON.stringify(v).slice(0, 80)}`) .join('\n'), @@ -362,24 +370,46 @@ export function DataIngestModule() { return; } - listEl.innerHTML = files.map((f) => ` -
-
-
- ${f.type} -

${f.filename}

-

${new Date(f.uploadedAt).toLocaleDateString()}

+ listEl.innerHTML = files.map((f) => { + const parseError = f.structuredData?.parse_error; + let cardFooter = ''; + if (parseError === 'no_api_key') { + cardFooter = ` +
+ ⚙ Add API key in Settings to extract + +
`; + } else if (parseError === 'ai_error') { + cardFooter = ` +
+ AI extraction failed + +
`; + } else if (f.preview) { + cardFooter = `
${f.preview}
`; + } + const fieldLink = this._hasFieldData(f) + ? `

↗ Contains field data ·

` + : ''; + + return ` +
+
+
+ ${f.type} +

${f.filename}

+

${new Date(f.uploadedAt).toLocaleDateString()}

+
+
- -
- ${f.preview ? `
${f.preview}
` : ''} - ${this._hasFieldData(f) ? `

↗ Contains field data ·

` : ''} -
- `).join(''); + ${cardFooter} + ${fieldLink} +
`; + }).join(''); listEl.querySelectorAll('.file-delete-btn').forEach((btn) => { btn.addEventListener('click', async () => { @@ -394,6 +424,64 @@ export function DataIngestModule() { if (file) await this._offerFieldImport(file, container); }); }); + + listEl.querySelectorAll('.reextract-btn').forEach((btn) => { + btn.addEventListener('click', async () => { + const file = files.find((f) => f.id === btn.dataset.id); + if (file) await this._reExtractFile(file, container); + }); + }); + }, + + async _reExtractFile(file, container) { + const status = container.querySelector('#ingest-status'); + const rawText = file.structuredData?.raw_text ?? file.structuredData?.raw_preview ?? ''; + + if (!rawText) { + status.textContent = 'No cached text — please re-upload the file.'; + setTimeout(() => { status.textContent = ''; }, 4000); + return; + } + + status.textContent = `Re-extracting ${file.filename}…`; + let structuredData = null; + + try { + const raw = await callAnthropic({ + system: 'You are an agricultural data analyst. Extract and return structured JSON from this document. Identify: operation type, field names (as "fields" array of strings), dates, quantities, equipment, crop types, financial figures, and any carbon or emissions data. For harvest data include avg_yield_bu_ac, avg_moisture_pct, harvest_date, and crop. Return only valid JSON.', + userMessage: rawText.slice(0, 6000), + maxTokens: 1024, + }); + structuredData = JSON.parse(raw); + } catch (err) { + const isNoKey = err.message?.toLowerCase().includes('no api key') || + err.message?.toLowerCase().includes('api key set'); + status.textContent = isNoKey + ? '⚙ API key required — open Settings (gear icon) to add your Anthropic key.' + : `Extraction failed: ${err.message.slice(0, 80)}`; + status.style.color = '#f87171'; + setTimeout(() => { status.textContent = ''; status.style.color = ''; }, 6000); + return; + } + + const updated = { + ...file, + structuredData, + preview: Object.entries(structuredData) + .filter(([k]) => k !== 'raw_text' && k !== 'raw_preview') + .slice(0, 5) + .map(([k, v]) => `${k}: ${JSON.stringify(v).slice(0, 80)}`) + .join('\n'), + }; + + await saveIngestedFile(updated); + + status.textContent = `✓ Extracted ${file.filename}`; + status.style.color = '#4ade80'; + setTimeout(() => { status.textContent = ''; status.style.color = ''; }, 3000); + + await this._renderFileList(container); + await this._offerFieldImport(updated, container); }, _hasFieldData(file) { diff --git a/agrifine-extension/src/utils/storage.js b/agrifine-extension/src/utils/storage.js index 5c0007a0cf..18f47d71dd 100644 --- a/agrifine-extension/src/utils/storage.js +++ b/agrifine-extension/src/utils/storage.js @@ -224,7 +224,7 @@ export async function buildContextBundle() { const fileLines = files.length === 0 ? ['(none)'] : files.slice(0, 10).map((f) => { const preview = f.structuredData ? Object.entries(f.structuredData) - .filter(([k]) => k !== 'raw_preview' && k !== 'parse_error') + .filter(([k]) => k !== 'raw_preview' && k !== 'raw_text' && k !== 'parse_error') .slice(0, 5) .map(([k, v]) => `${k}: ${JSON.stringify(v).slice(0, 120)}`) .join(' | ')