Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
81b903243e |
@@ -0,0 +1,282 @@
|
||||
# Hermes Agent — Dependency License Audit
|
||||
|
||||
**Date:** 2026-07-20
|
||||
**Project license:** MIT (Copyright 2025 Nous Research)
|
||||
**Audited by:** ethie (automated — PyPI/npm/GitHub registry queries + lockfile parsing)
|
||||
|
||||
## Scope
|
||||
|
||||
Every dependency that ships in a Hermes Agent distribution, across all three
|
||||
ecosystems and all four npm lockfiles:
|
||||
|
||||
| Ecosystem | Lockfile | Packages |
|
||||
|-----------|----------|----------|
|
||||
| Python | `uv.lock` | 233 |
|
||||
| npm (root workspaces) | `package-lock.json` | 1,387 |
|
||||
| npm (website / Docusaurus) | `website/package-lock.json` | 1,443 |
|
||||
| npm (whatsapp-bridge) | `scripts/whatsapp-bridge/package-lock.json` | 167 |
|
||||
| npm (photon sidecar) | `plugins/platforms/photon/sidecar/package-lock.json` | 144 |
|
||||
| npm (deduped across all 4) | — | 2,401 unique |
|
||||
| Rust | `apps/bootstrap-installer/src-tauri/Cargo.toml` | 19 direct (no `Cargo.lock`) |
|
||||
|
||||
**Total: 2,653 dependency versions audited.**
|
||||
|
||||
## Executive summary
|
||||
|
||||
The overwhelming majority of dependencies (2,564 / 2,653 = 96.6%) are under
|
||||
permissive licenses (MIT, ISC, Apache-2.0, BSD, PSF) that are fully
|
||||
compatible with the project's MIT license. No action is needed for those.
|
||||
|
||||
**9 packages require attention** — they fall into copyleft, attribution-required,
|
||||
proprietary, or unlicensed categories that create real compliance obligations.
|
||||
The most serious is **`libsignal` (GPL-3.0)** in the whatsapp-bridge, which has
|
||||
strong copyleft implications if the bridge is distributed as part of Hermes.
|
||||
|
||||
**There is currently zero license-compliance scanning in CI.** The existing
|
||||
`osv-scanner.yml` covers CVEs only; `supply-chain-audit.yml` covers malware
|
||||
patterns only. Neither checks license compatibility. There is no `NOTICE` or
|
||||
`THIRD_PARTY_LICENSES` file at the repo root (the only `NOTICE` is inside
|
||||
`plugins/security-guidance/` for forked Anthropic Apache-2.0 code).
|
||||
|
||||
---
|
||||
|
||||
## Current compliance posture
|
||||
|
||||
| What exists | What it covers | Gap |
|
||||
|-------------|----------------|-----|
|
||||
| `osv-scanner.yml` | Known CVEs in pinned deps (vuln DB) | Does not check licenses |
|
||||
| `supply-chain-audit.yml` | Malware patterns in PR diffs (.pth, base64+exec, etc.) | Does not check licenses |
|
||||
| `tests/docker/test_license_file_present.py` | Our own LICENSE ships in the Docker image | Only checks our LICENSE, not deps |
|
||||
| `plugins/security-guidance/NOTICE` | Forked Anthropic code attribution | Local to that plugin only |
|
||||
| **Missing** | **License compatibility scanning** | **No CI check prevents adding a GPL/AGPL/unlicensed dep** |
|
||||
| **Missing** | **NOTICE / THIRD_PARTY_LICENSES file** | **No bundled attribution for permissive licenses that require it** |
|
||||
|
||||
---
|
||||
|
||||
## Findings by severity
|
||||
|
||||
### 🔴 Critical — GPL-3.0 (strong copyleft)
|
||||
|
||||
#### `libsignal@6.0.0` (npm, whatsapp-bridge)
|
||||
|
||||
- **License:** GPL-3.0
|
||||
- **Source:** `https://github.com/WhiskeySockets/libsignal-node`
|
||||
- **Where:** `scripts/whatsapp-bridge/package.json` → `@whiskeysockets/baileys` depends on it
|
||||
- **Direct dep?** No — transitive via `@whiskeysockets/baileys`
|
||||
- **Compliance issue:** GPL-3.0 is strong copyleft. If the whatsapp-bridge is
|
||||
distributed as part of Hermes Agent (which is MIT), the GPL-3.0 license
|
||||
would require the *entire combined work* to be GPL-3.0. This is a
|
||||
license conflict — MIT and GPL-3.0 are compatible (MIT code can be included
|
||||
in a GPL-3.0 project), but only if the combined work is distributed under
|
||||
GPL-3.0, which contradicts Hermes's MIT license.
|
||||
- **Mitigation:** The whatsapp-bridge is a standalone sidecar script
|
||||
(`scripts/whatsapp-bridge/bridge.js`), not imported by the Python core.
|
||||
If it ships as a separate process invoked via subprocess (not linked
|
||||
into the Hermes binary), the "aggregate, not derivative" argument may
|
||||
apply. **This needs a legal review** to confirm the boundary.
|
||||
|
||||
### 🟠 High — LGPL-3.0 (weak copyleft)
|
||||
|
||||
LGPL allows linking from non-LGPL code, but modifications to the LGPL
|
||||
library itself must be released under LGPL, and the license + source
|
||||
must be offered to recipients.
|
||||
|
||||
#### Python
|
||||
|
||||
| Package | Version | Extra | Notes |
|
||||
|---------|---------|-------|-------|
|
||||
| `edge-tts` | 7.2.7 | `[edge-tts]` | Default TTS provider. LICENSE file confirms: one file (srt_composer.py) is MIT, rest is LGPLv3. |
|
||||
| `python-telegram-bot` | 22.6 | `[messaging]`, `[termux]` | PEP 639 `license_expression: LGPL-3.0-only`. LICENSE file ships GPL-3 text (LGPL is a permitted additional permission). |
|
||||
|
||||
#### npm (sharp / libvips native binaries)
|
||||
|
||||
| Package | Version | License | Notes |
|
||||
|---------|---------|---------|-------|
|
||||
| `@img/sharp-libvips-*` (10 platform variants) | 1.3.2 | LGPL-3.0-or-later | libvips native binaries. Pulled by `sharp` (image processing). |
|
||||
| `@img/sharp-win32-*` (3 variants) | 0.35.3 | Apache-2.0 AND LGPL-3.0-or-later | sharp Windows binaries. |
|
||||
| `@img/sharp-wasm32` | 0.35.3 | Apache-2.0 AND LGPL-3.0-or-later AND MIT | sharp wasm binary. |
|
||||
|
||||
- **Where:** Root `package-lock.json`, transitive via `sharp`
|
||||
- **Direct dep?** No — `sharp` is not declared in any workspace `package.json`;
|
||||
it's pulled transitively by an upstream package.
|
||||
- **Compliance issue:** LGPL-3.0-or-later allows use in non-LGPL projects,
|
||||
but requires: (1) providing the LGPL license text, (2) providing a way to
|
||||
relink against a modified version of the library. For statically-linked
|
||||
native binaries, this means documenting how to swap the .node/.so/.dll.
|
||||
- **Mitigation:** Include LGPL license text in NOTICE file. The "provide a
|
||||
way to relink" obligation is satisfied by the npm package boundary (users
|
||||
can `npm install` a different `sharp` build).
|
||||
|
||||
### 🟡 Medium — MPL-2.0 / MPL-1.1 (file-level weak copyleft)
|
||||
|
||||
MPL is file-level copyleft: modifications to MPL-licensed *files* must stay
|
||||
MPL, but it does not infect the rest of the project. Compatible with MIT.
|
||||
|
||||
| Package | Version | Ecosystem | Notes |
|
||||
|---------|---------|-----------|-------|
|
||||
| `lightningcss` (+ 11 platform variants) | 1.32.0 | npm | CSS transformer, pulled by `@tailwindcss` + `vite`. |
|
||||
| `lunr-languages` | 1.14.0 | npm (website) | Language packs for Docusaurus search. Old MPL-1.1. |
|
||||
| `certifi` | 2026.5.20 | python | CA bundle. |
|
||||
| `mautrix` | 0.21.0 | python | Matrix SDK (`[matrix]` extra). |
|
||||
| `pathspec` | 1.1.1 | python | .gitignore matching. Core dep. |
|
||||
|
||||
- **Compliance:** Include MPL license text in NOTICE. No source disclosure
|
||||
obligation. If we modify any MPL file, that file must stay MPL.
|
||||
|
||||
### 🟡 Medium — CC-BY-4.0 (attribution required)
|
||||
|
||||
CC-BY-4.0 requires attribution in a way that is "reasonable to the medium."
|
||||
For software distribution, this means the NOTICE file.
|
||||
|
||||
| Package | Version | Ecosystem | Notes |
|
||||
|---------|---------|-----------|-------|
|
||||
| `@vscode/codicons` | 0.0.45 | npm | Icon font, direct dep in `apps/desktop` + `apps/bootstrap-installer`. |
|
||||
| `caniuse-lite` | 1.0.30001799 | npm | Browser support DB, transitive via `browserslist`. |
|
||||
|
||||
- **Compliance:** Add attribution lines to NOTICE file.
|
||||
|
||||
### 🟡 Medium — Non-standard / proprietary license
|
||||
|
||||
#### `gsap@3.15.0` (npm)
|
||||
|
||||
- **License:** "Standard 'no charge' license: https://gsap.com/standard-license"
|
||||
- **Where:** `web/package.json` (direct dep), also pulled by `@nous-research/ui`
|
||||
- **Compliance issue:** This is not an OSI-approved license. GSAP's own terms
|
||||
apply. Per gsap.com: "GSAP is now free for everyone, thanks to Webflow's
|
||||
support!" — but the license has specific terms (no resale as a standalone
|
||||
library, etc.) that differ from MIT.
|
||||
- **Compliance:** Reproduce GSAP's license terms in the NOTICE file with a
|
||||
link to the full text.
|
||||
|
||||
### 🟢 Low — No license (all-rights-reserved)
|
||||
|
||||
| Package | Version | Ecosystem | Notes |
|
||||
|---------|---------|-----------|-------|
|
||||
| `hindsight-client` | 0.6.1 | python | Optional `[hindsight]` extra. No LICENSE in wheel, no repo link. |
|
||||
| `format` | 0.2.2 | npm (website) | sprintf library. No LICENSE anywhere. Transitive via Docusaurus. |
|
||||
|
||||
- **Compliance issue:** Without a license, the default is "all rights reserved"
|
||||
— the copyright holder has not granted permission to use, modify, or
|
||||
distribute. Technically, using these at all is a copyright infringement.
|
||||
- **Mitigation:**
|
||||
- `hindsight-client`: Contact the "Hindsight Team" for a license
|
||||
clarification, or remove the extra and document it as user-installed.
|
||||
- `format`: It's a tiny, ancient (2012-era) sprintf utility pulled
|
||||
transitively by Docusaurus. Contact the author (samsonjs) or replace
|
||||
with a licensed alternative. Alternatively, Docusaurus may have already
|
||||
addressed this upstream.
|
||||
|
||||
### ✅ Resolved (were "missing" in metadata, verified permissive)
|
||||
|
||||
These packages had no license in their npm/PyPI metadata but were verified
|
||||
permissive by fetching their LICENSE files from GitHub or inspecting tarballs:
|
||||
|
||||
| Package | Resolved license | How verified |
|
||||
|---------|-----------------|--------------|
|
||||
| `khroma` | MIT | GitHub `fabiospampinato/khroma` LICENSE file |
|
||||
| `eval` | MIT | GitHub `pierrec/node-eval` LICENSE file |
|
||||
| `require-like` | MIT | GitHub `felixge/node-require-like` package.json |
|
||||
| `qrcode-terminal` | MIT | GitHub `gtanner/qrcode-terminal` LICENSE file |
|
||||
| `@photon-ai/slack` | MIT | Tarball `package/proto/LICENSE`: "Copyright (c) 2025 Photon AI" |
|
||||
| `@photon-ai/whatsapp-business` | Likely MIT | Same Photon AI scope; no LICENSE in tarball (unverified) |
|
||||
| `agent-client-protocol` | Apache-2.0 | GitHub `agentclientprotocol/python-sdk` LICENSE |
|
||||
| `azure-core` | MIT | GitHub `Azure/azure-sdk-for-python` LICENSE |
|
||||
| `azure-identity` | MIT | GitHub `Azure/azure-sdk-for-python` LICENSE |
|
||||
| `fal-client` | Apache-2.0 | GitHub `fal-ai/fal` LICENSE |
|
||||
| `mistralai` | Apache-2.0 | GitHub `mistralai/client-python` LICENSE |
|
||||
| `microsoft-teams-*` (4 pkgs) | MIT | GitHub `microsoft/teams.py` LICENSE |
|
||||
| `honcho-ai` | Apache-2.0 | PyPI `license_expression` field |
|
||||
|
||||
### ✅ No issue (compound permissive licenses)
|
||||
|
||||
| Package | License | Why it's fine |
|
||||
|---------|---------|---------------|
|
||||
| `@bufbuild/protobuf` | Apache-2.0 AND BSD-3-Clause | Both permissive |
|
||||
| `dompurify` | MPL-2.0 OR Apache-2.0 | Can elect Apache-2.0 |
|
||||
|
||||
---
|
||||
|
||||
## Rust (Cargo)
|
||||
|
||||
No `Cargo.lock` exists in the repo, so a transitive audit is deferred.
|
||||
All 19 direct dependencies in `apps/bootstrap-installer/src-tauri/Cargo.toml`
|
||||
are permissive (MIT or Apache-2.0 or both). Run `cargo generate-lockfile`
|
||||
before the installer is first built/distributed to enable the transitive check.
|
||||
|
||||
---
|
||||
|
||||
## Compliance plan
|
||||
|
||||
### 1. Create a root `NOTICE` file
|
||||
|
||||
The biggest gap. Many permissive licenses (MIT, Apache-2.0, BSD) require
|
||||
retaining the copyright + license notice when distributed. Currently we ship
|
||||
our own `LICENSE` but don't bundle attribution for dependencies.
|
||||
|
||||
**Action:** Generate `NOTICE` (or `THIRD_PARTY_LICENSES.md`) at repo root,
|
||||
containing:
|
||||
- The project's own MIT license + copyright
|
||||
- For each non-MIT dependency: the license name, copyright holder, and
|
||||
license text (or link)
|
||||
- Attribution blocks for CC-BY-4.0 packages (codicons, caniuse-lite)
|
||||
- Reproduced terms for GSAP's non-standard license
|
||||
- LGPL/MPL license texts for the copyleft deps
|
||||
|
||||
This can be auto-generated from the lockfiles using a tool like
|
||||
`pip-licenses` (Python) + `license-checker` (npm) in CI.
|
||||
|
||||
### 2. Add license-scanning CI
|
||||
|
||||
**Action:** Add a `license-audit.yml` workflow that:
|
||||
- Runs `pip-licenses` (or `uv pip licenses`) against `uv.lock`
|
||||
- Runs `license-checker` (or `licensee`) against all 4 `package-lock.json` files
|
||||
- Fails on: GPL, AGPL, unlisted, or "unknown" licenses
|
||||
- Warns on: LGPL, MPL, CC-BY, non-OSI licenses (advisory, not blocking)
|
||||
- Posts findings as a collapsible `<details>` PR comment
|
||||
|
||||
This closes the gap where a PR could add a GPL-3.0 dependency and CI would
|
||||
never catch it.
|
||||
|
||||
### 3. Resolve the `libsignal` GPL-3.0 question
|
||||
|
||||
**Action:** Legal review to confirm whether `scripts/whatsapp-bridge/` is a
|
||||
separate aggregate (not a derivative of Hermes) when distributed. If the
|
||||
bridge ships as a standalone subprocess invoked via `node bridge.js`, the
|
||||
GPL-3.0 obligation may not extend to Hermes itself. If there's any doubt,
|
||||
document the boundary clearly or move the bridge to a separate repo.
|
||||
|
||||
### 4. Resolve unlicensed packages
|
||||
|
||||
**Action:**
|
||||
- `hindsight-client`: Contact the Hindsight Team for a license, or move the
|
||||
`[hindsight]` extra to lazy-install only (it already is, partially).
|
||||
- `format`: Check if Docusaurus has dropped or replaced it upstream. If not,
|
||||
file an issue with samsonjs/format to add a license, or patch it out.
|
||||
|
||||
### 5. Generate `Cargo.lock` for the bootstrap installer
|
||||
|
||||
**Action:** Run `cargo generate-lockfile` in
|
||||
`apps/bootstrap-installer/src-tauri/` and commit it, so the transitive Rust
|
||||
dependency tree is auditable before the installer is distributed.
|
||||
|
||||
### 6. Document the MPL/LGPL boundary
|
||||
|
||||
**Action:** Add a section to `CONTRIBUTING.md` documenting that:
|
||||
- MPL-2.0 deps are acceptable (file-level copyleft, MIT-compatible) but
|
||||
modifications to MPL files must stay MPL
|
||||
- LGPL-3.0 deps are acceptable (linking is fine) but the NOTICE file must
|
||||
carry the LGPL text and document how to relink
|
||||
- GPL/AGPL deps are NOT acceptable in the core or any workspace that ships
|
||||
with Hermes — they must be isolated in standalone sidecar processes
|
||||
|
||||
---
|
||||
|
||||
## Appendix: tools used
|
||||
|
||||
This audit was performed with:
|
||||
- `uv.lock` parsing (233 Python packages, PEP 639 `license_expression` field)
|
||||
- `package-lock.json` parsing (4 lockfiles, 2,401 unique npm packages)
|
||||
- PyPI JSON API (`/pypi/{name}/{version}/json`) for Python license metadata
|
||||
- npm registry API (`registry.npmjs.org`) for npm license metadata
|
||||
- GitHub raw (`raw.githubusercontent.com`) for LICENSE file verification
|
||||
- npm tarball inspection for packages with no metadata
|
||||
@@ -8521,6 +8521,47 @@ ipcMain.handle('hermes:readFileText', async (_event, filePath) => {
|
||||
}
|
||||
})
|
||||
|
||||
// Read a bundled license file from the app's extraResources directory.
|
||||
// The desktop build generates dependencies.txt (JS deps) and
|
||||
// dependencies-python.txt (Python deps) via the generate-licenses scripts,
|
||||
// and electron-builder ships them via extraResources so they land at
|
||||
// process.resourcesPath/<filename> in the packaged app. In dev (running
|
||||
// from a checkout), they live in apps/desktop/dist/<filename>.
|
||||
//
|
||||
// Returns the file contents as a string, or null if the file is missing.
|
||||
// The Settings → Licenses page uses this to display third-party license
|
||||
// attributions required by MIT/Apache/BSD licenses.
|
||||
ipcMain.handle('hermes:readBundledLicenseFile', async (_event, filename: string) => {
|
||||
if (!filename || typeof filename !== 'string') {
|
||||
return null
|
||||
}
|
||||
|
||||
// Only allow plain filenames — no path traversal.
|
||||
const safe = path.basename(filename)
|
||||
if (safe !== filename) {
|
||||
return null
|
||||
}
|
||||
|
||||
// electron-builder extraResources place files directly in resources/.
|
||||
// The Nix package preserves the renderer's dist/ directory instead, so
|
||||
// support both layouts plus the local dev build location.
|
||||
const candidates = [
|
||||
process.resourcesPath ? path.join(process.resourcesPath, safe) : null,
|
||||
process.resourcesPath ? path.join(process.resourcesPath, 'dist', safe) : null,
|
||||
path.join(APP_ROOT, 'dist', safe)
|
||||
].filter(Boolean)
|
||||
|
||||
for (const p of candidates) {
|
||||
try {
|
||||
return await fs.promises.readFile(p, 'utf8')
|
||||
} catch {
|
||||
// ENOENT — try next candidate
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
})
|
||||
|
||||
ipcMain.handle('hermes:selectPaths', async (_event, options: any = {}) => {
|
||||
const properties = options?.directories ? ['openDirectory'] : ['openFile']
|
||||
|
||||
|
||||
@@ -61,6 +61,7 @@ contextBridge.exposeInMainWorld('hermesDesktop', {
|
||||
requestMicrophoneAccess: () => ipcRenderer.invoke('hermes:requestMicrophoneAccess'),
|
||||
readFileDataUrl: filePath => ipcRenderer.invoke('hermes:readFileDataUrl', filePath),
|
||||
readFileText: filePath => ipcRenderer.invoke('hermes:readFileText', filePath),
|
||||
readBundledLicenseFile: filename => ipcRenderer.invoke('hermes:readBundledLicenseFile', filename),
|
||||
selectPaths: options => ipcRenderer.invoke('hermes:selectPaths', options),
|
||||
writeClipboard: text => ipcRenderer.invoke('hermes:writeClipboard', text),
|
||||
saveImageFromUrl: url => ipcRenderer.invoke('hermes:saveImageFromUrl', url),
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
"profile:main:cpu": "wait-on http://127.0.0.1:5174 && node scripts/bundle-electron-main.mjs --dev && cross-env XCURSOR_SIZE=24 NODE_OPTIONS=--cpu-prof HERMES_DESKTOP_DEV_SERVER=http://127.0.0.1:5174 electron .",
|
||||
"start": "npm run build && electron .",
|
||||
"prebuild": "tsc -b . --clean",
|
||||
"build": "node scripts/assert-root-install.mjs && node scripts/write-build-stamp.mjs && vite build && node scripts/bundle-electron-main.mjs && node scripts/stage-native-deps.mjs",
|
||||
"build": "node scripts/assert-root-install.mjs && node scripts/write-build-stamp.mjs && vite build && npm run generate-licenses && node scripts/bundle-electron-main.mjs && node scripts/stage-native-deps.mjs",
|
||||
"postbuild": "node scripts/assert-dist-built.mjs",
|
||||
"prebuilder": "node scripts/patch-electron-builder-mac-binary.mjs",
|
||||
"builder": "cross-env NODE_OPTIONS=--max-old-space-size=16384 node scripts/run-electron-builder.mjs",
|
||||
@@ -53,7 +53,10 @@
|
||||
"check": "npm run typecheck && npm run test && npm run test:desktop:all",
|
||||
"test:e2e": "playwright test e2e/",
|
||||
"test:e2e:visual": "WLR_BACKENDS=headless WLR_NO_HARDWARE_CURSORS=1 cage -- npx playwright test e2e/ --reporter=list",
|
||||
"test:e2e:update-snapshots": "WLR_BACKENDS=headless WLR_NO_HARDWARE_CURSORS=1 cage -- npx playwright test e2e/ --reporter=list --update-snapshots"
|
||||
"test:e2e:update-snapshots": "WLR_BACKENDS=headless WLR_NO_HARDWARE_CURSORS=1 cage -- npx playwright test e2e/ --reporter=list --update-snapshots",
|
||||
"generate-licenses": "node scripts/generate-js-licenses.mjs && node scripts/generate-python-licenses.mjs",
|
||||
"generate-licenses:js": "node scripts/generate-js-licenses.mjs",
|
||||
"generate-licenses:python": "node scripts/generate-python-licenses.mjs"
|
||||
},
|
||||
"dependencies": {
|
||||
"@assistant-ui/react": "^0.14.23",
|
||||
@@ -151,6 +154,7 @@
|
||||
"eslint-plugin-unused-imports": "^4.4.1",
|
||||
"globals": "^16.5.0",
|
||||
"jsdom": "^29.1.1",
|
||||
"license-checker": "^25.0.1",
|
||||
"prettier": "^3.8.3",
|
||||
"rcedit": "^5.0.2",
|
||||
"tsx": "^4.22.4",
|
||||
@@ -194,6 +198,14 @@
|
||||
{
|
||||
"from": "assets/icon.ico",
|
||||
"to": "icon.ico"
|
||||
},
|
||||
{
|
||||
"from": "dist/dependencies.txt",
|
||||
"to": "dependencies.txt"
|
||||
},
|
||||
{
|
||||
"from": "dist/dependencies-python.txt",
|
||||
"to": "dependencies-python.txt"
|
||||
}
|
||||
],
|
||||
"asar": true,
|
||||
|
||||
@@ -0,0 +1,182 @@
|
||||
// Generates a bundled third-party license inventory for the desktop app's
|
||||
// JavaScript dependencies.
|
||||
//
|
||||
// Scans the installed node_modules tree with license-checker, then emits
|
||||
// dist/dependencies.txt — a plain-text file containing each package's
|
||||
// name, version, license, repository URL, and full license text when
|
||||
// available. This file is shipped in the electron-builder app bundle via
|
||||
// extraResources and surfaced in the Settings → Licenses page.
|
||||
//
|
||||
// Why license-checker (standalone CLI) instead of rollup-plugin-license:
|
||||
// Vite 8 uses rolldown under the hood, and rollup-plugin-license is a
|
||||
// rollup plugin — compatibility is uncertain and the plugin approach
|
||||
// only covers deps that end up in the bundle. license-checker scans the
|
||||
// full node_modules tree, so the inventory is complete even for deps
|
||||
// that are never imported at runtime (native modules, etc.).
|
||||
import { execFileSync } from 'node:child_process'
|
||||
import { mkdirSync, writeFileSync, existsSync, readFileSync } from 'node:fs'
|
||||
import path from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
||||
// __dirname = apps/desktop/scripts/
|
||||
// repoRoot = apps/desktop/scripts/../../.. = repo root
|
||||
const repoRoot = path.resolve(__dirname, '..', '..', '..')
|
||||
const distDir = path.resolve(__dirname, '..', 'dist')
|
||||
|
||||
// We scan from the repo root, not the desktop workspace, because npm
|
||||
// workspaces hoist all dependencies to the root node_modules/. Scanning
|
||||
// from apps/desktop only finds the handful of deps that have their own
|
||||
// node_modules (non-hoisted native modules etc.), while the repo root
|
||||
// captures the full production dependency tree (77+ packages).
|
||||
const SCAN_ROOT = repoRoot
|
||||
|
||||
function runLicenseChecker() {
|
||||
const bin = path.join(repoRoot, 'node_modules', '.bin', 'license-checker')
|
||||
// --production: exclude devDependencies — the shipped app only carries
|
||||
// production deps in its bundle, so the license inventory matches.
|
||||
// --json: structured output for easy post-processing.
|
||||
// --relativeLicensePath: emit paths to LICENSE files so we can read them.
|
||||
const args = ['--production', '--json', '--relativeLicensePath']
|
||||
const raw = execFileSync(bin, args, {
|
||||
cwd: SCAN_ROOT,
|
||||
encoding: 'utf8',
|
||||
maxBuffer: 50 * 1024 * 1024,
|
||||
stdio: ['pipe', 'pipe', 'pipe'],
|
||||
})
|
||||
return JSON.parse(raw)
|
||||
}
|
||||
|
||||
function readLicenseFile(relPath) {
|
||||
if (!relPath) {
|
||||
return null
|
||||
}
|
||||
|
||||
// license-checker returns paths relative to the scan root (cwd).
|
||||
// In a workspace, these look like "node_modules/foo/LICENSE" and
|
||||
// resolve correctly from the workspace root.
|
||||
const full = path.resolve(SCAN_ROOT, relPath)
|
||||
if (!existsSync(full)) {
|
||||
return null
|
||||
}
|
||||
|
||||
try {
|
||||
return readFileSync(full, 'utf8').trim()
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
// Format the inventory as a human-readable plain-text file. Each package
|
||||
// gets a delimited block with its metadata + full license text, so the
|
||||
// result is a self-contained NOTICE-equivalent.
|
||||
function generatedAt() {
|
||||
// Nix sets SOURCE_DATE_EPOCH for reproducible builds. Local builds retain a
|
||||
// useful wall-clock timestamp when that conventional variable is absent.
|
||||
const epoch = Number(process.env.SOURCE_DATE_EPOCH)
|
||||
return new Date(Number.isFinite(epoch) && epoch > 0 ? epoch * 1000 : Date.now()).toISOString()
|
||||
}
|
||||
|
||||
function formatInventory(entries) {
|
||||
const lines = []
|
||||
|
||||
lines.push('Third-Party Software Licenses')
|
||||
lines.push('=============================')
|
||||
lines.push('')
|
||||
lines.push('This file lists the open-source licenses of the npm dependencies')
|
||||
lines.push('bundled with the Hermes desktop application.')
|
||||
lines.push('')
|
||||
lines.push(`Generated: ${generatedAt()}`)
|
||||
lines.push(`Total packages: ${entries.length}`)
|
||||
lines.push('')
|
||||
|
||||
for (const entry of entries) {
|
||||
lines.push('─'.repeat(70))
|
||||
lines.push(`Package: ${entry.name}@${entry.version}`)
|
||||
lines.push(`License: ${entry.license}`)
|
||||
if (entry.repository) {
|
||||
lines.push(`Repository: ${entry.repository}`)
|
||||
}
|
||||
if (entry.publisher) {
|
||||
lines.push(`Publisher: ${entry.publisher}`)
|
||||
}
|
||||
if (entry.email) {
|
||||
lines.push(`Email: ${entry.email}`)
|
||||
}
|
||||
lines.push('')
|
||||
if (entry.licenseText) {
|
||||
lines.push(entry.licenseText)
|
||||
} else {
|
||||
lines.push('(No license text file found — see the package repository for details.)')
|
||||
}
|
||||
lines.push('')
|
||||
}
|
||||
|
||||
return lines.join('\n')
|
||||
}
|
||||
|
||||
function main() {
|
||||
mkdirSync(distDir, { recursive: true })
|
||||
|
||||
let data
|
||||
try {
|
||||
data = runLicenseChecker()
|
||||
} catch (e) {
|
||||
// license-checker not installed or failed — write a stub so the build
|
||||
// doesn't fail and the Settings page can show a graceful message.
|
||||
const stub = [
|
||||
'Third-Party Software Licenses',
|
||||
'=============================',
|
||||
'',
|
||||
'WARNING: license-checker was not found or failed to run.',
|
||||
'The JavaScript dependency license inventory could not be generated.',
|
||||
'',
|
||||
`Error: ${e.message}`,
|
||||
'',
|
||||
].join('\n')
|
||||
writeFileSync(path.join(distDir, 'dependencies.txt'), stub, 'utf8')
|
||||
console.warn(`[generate-js-licenses] license-checker failed: ${e.message}`)
|
||||
return
|
||||
}
|
||||
|
||||
// Dedupe by name@version — the same package can appear once.
|
||||
const seen = new Map()
|
||||
|
||||
for (const [pkgKey, info] of Object.entries(data)) {
|
||||
// pkgKey is "name@version"
|
||||
const atIdx = pkgKey.lastIndexOf('@')
|
||||
const name = atIdx > 0 ? pkgKey.slice(0, atIdx) : pkgKey
|
||||
const version = atIdx > 0 ? pkgKey.slice(atIdx + 1) : ''
|
||||
|
||||
const dedupeKey = `${name}@${version}`
|
||||
if (seen.has(dedupeKey)) {
|
||||
continue
|
||||
}
|
||||
|
||||
const licenseText = info.licenseFile
|
||||
? readLicenseFile(info.licenseFile)
|
||||
: null
|
||||
|
||||
seen.set(dedupeKey, {
|
||||
name,
|
||||
version,
|
||||
license: info.licenses || info.license || 'UNKNOWN',
|
||||
repository: info.repository || null,
|
||||
publisher: info.publisher || null,
|
||||
email: info.email || null,
|
||||
licenseText,
|
||||
})
|
||||
}
|
||||
|
||||
const entries = [...seen.values()].sort((a, b) =>
|
||||
a.name.toLowerCase().localeCompare(b.name.toLowerCase()),
|
||||
)
|
||||
|
||||
const output = formatInventory(entries)
|
||||
const outPath = path.join(distDir, 'dependencies.txt')
|
||||
writeFileSync(outPath, output, 'utf8')
|
||||
|
||||
console.log(`[generate-js-licenses] Wrote ${entries.length} packages to ${outPath}`)
|
||||
}
|
||||
|
||||
main()
|
||||
@@ -0,0 +1,181 @@
|
||||
// Generates a bundled third-party license inventory for the Hermes Agent
|
||||
// Python dependencies.
|
||||
//
|
||||
// Shells out to `pip-licenses` against the active Python environment,
|
||||
// emitting dist/dependencies-python.txt — a plain-text file containing
|
||||
// each package's name, version, license, and full license text (when
|
||||
// available in the installed dist-info).
|
||||
//
|
||||
// pip-licenses reads from the installed environment, so the inventory
|
||||
// reflects whatever extras are actually installed on the build machine.
|
||||
// --from=mixed is critical: it reads both the legacy License classifier
|
||||
// AND the PEP 639 license_expression field, which is where modern
|
||||
// packages (cryptography, pydantic, fastapi, etc.) declare their SPDX
|
||||
// license. Without --from=mixed, ~30% of packages show as UNKNOWN.
|
||||
import { execFileSync } from 'node:child_process'
|
||||
import { mkdirSync, writeFileSync, existsSync } from 'node:fs'
|
||||
import path from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
||||
// __dirname = apps/desktop/scripts/; three parents is the repository root.
|
||||
const repoRoot = path.resolve(__dirname, '..', '..', '..')
|
||||
const distDir = path.resolve(__dirname, '..', 'dist')
|
||||
|
||||
// Locate the Python environment to scan. Resolution order:
|
||||
// 1. HERMES_PYTHON (Nix dev shell / Nix desktop build)
|
||||
// 2. VIRTUAL_ENV (explicit activated venv)
|
||||
// 3. repo-root .venv or venv (local dev worktree)
|
||||
// 4. HERMES_HOME/hermes-agent/venv (managed install, matches main.ts)
|
||||
// 5. pip-licenses on PATH (fallback)
|
||||
function findPipLicensesBin() {
|
||||
// Nix provides the exact Python interpreter through HERMES_PYTHON. It is
|
||||
// a store path rather than a traditional venv directory, so looking for
|
||||
// bin/pip-licenses next to it does not work; invoke the installed module.
|
||||
if (process.env.HERMES_PYTHON && existsSync(process.env.HERMES_PYTHON)) {
|
||||
return { type: 'module', path: process.env.HERMES_PYTHON, venv: null }
|
||||
}
|
||||
|
||||
// pip-licenses is a console script installed into the venv's bin/Scripts
|
||||
// dir. We look for it relative to candidate venv roots.
|
||||
const binName = process.platform === 'win32' ? 'pip-licenses.exe' : 'pip-licenses'
|
||||
const scriptName = process.platform === 'win32' ? 'pip-licenses-script.py' : 'pip-licenses'
|
||||
|
||||
const candidates = []
|
||||
|
||||
if (process.env.VIRTUAL_ENV) {
|
||||
candidates.push(process.env.VIRTUAL_ENV)
|
||||
}
|
||||
|
||||
candidates.push(path.join(repoRoot, '.venv'))
|
||||
candidates.push(path.join(repoRoot, 'venv'))
|
||||
|
||||
if (process.env.HERMES_HOME) {
|
||||
candidates.push(path.join(process.env.HERMES_HOME, 'hermes-agent', 'venv'))
|
||||
}
|
||||
|
||||
// Check $HOME/.hermes/hermes-agent/venv (the managed-install default)
|
||||
candidates.push(path.join(process.env.HOME || '', '.hermes', 'hermes-agent', 'venv'))
|
||||
|
||||
for (const venv of candidates) {
|
||||
const binDir = process.platform === 'win32'
|
||||
? path.join(venv, 'Scripts')
|
||||
: path.join(venv, 'bin')
|
||||
const binPath = path.join(binDir, binName)
|
||||
const scriptPath = path.join(binDir, scriptName)
|
||||
|
||||
if (existsSync(binPath)) {
|
||||
return { type: 'bin', path: binPath, venv }
|
||||
}
|
||||
if (existsSync(scriptPath)) {
|
||||
// Need to run it with the venv's python
|
||||
const pythonBin = process.platform === 'win32'
|
||||
? path.join(venv, 'Scripts', 'python.exe')
|
||||
: path.join(venv, 'bin', 'python')
|
||||
if (existsSync(pythonBin)) {
|
||||
return { type: 'module', path: pythonBin, venv }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: assume pip-licenses is on PATH (CI/dev shells)
|
||||
return { type: 'path', path: 'pip-licenses', venv: null }
|
||||
}
|
||||
|
||||
function runPipLicenses() {
|
||||
const { type, path: binPath } = findPipLicensesBin()
|
||||
|
||||
// Flags:
|
||||
// --from=mixed — read PEP 639 license_expression AND legacy classifier
|
||||
// --with-license-file — include the full license text from the dist-info
|
||||
// --with-notice-file — include NOTICE files (Apache-2.0 §4(d) requirement)
|
||||
// --no-license-path — don't emit the on-disk path (we're bundling, not linking)
|
||||
// --format=plain-vertical — one package per block, readable as a NOTICE file
|
||||
//
|
||||
// Note: --with-license-file prints a warning about long fields with
|
||||
// plain-vertical format, but the output is still correct — the warning
|
||||
// is cosmetic. We suppress stderr to keep the build log clean.
|
||||
const args = [
|
||||
'--from=mixed',
|
||||
'--with-license-file',
|
||||
'--with-notice-file',
|
||||
'--no-license-path',
|
||||
'--format=plain-vertical',
|
||||
]
|
||||
|
||||
let result
|
||||
if (type === 'module') {
|
||||
// python -m piplicenses <args>
|
||||
result = execFileSync(binPath, ['-m', 'piplicenses', ...args], {
|
||||
encoding: 'utf8',
|
||||
maxBuffer: 50 * 1024 * 1024,
|
||||
stdio: ['pipe', 'pipe', 'pipe'],
|
||||
env: { ...process.env, PYTHONUNBUFFERED: '1' },
|
||||
})
|
||||
} else {
|
||||
// Direct binary or PATH lookup
|
||||
result = execFileSync(binPath, args, {
|
||||
encoding: 'utf8',
|
||||
maxBuffer: 50 * 1024 * 1024,
|
||||
stdio: ['pipe', 'pipe', 'pipe'],
|
||||
env: { ...process.env, PYTHONUNBUFFERED: '1' },
|
||||
})
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
function generatedAt() {
|
||||
// Nix sets SOURCE_DATE_EPOCH for reproducible builds. Local builds retain a
|
||||
// useful wall-clock timestamp when that conventional variable is absent.
|
||||
const epoch = Number(process.env.SOURCE_DATE_EPOCH)
|
||||
return new Date(Number.isFinite(epoch) && epoch > 0 ? epoch * 1000 : Date.now()).toISOString()
|
||||
}
|
||||
|
||||
function main() {
|
||||
mkdirSync(distDir, { recursive: true })
|
||||
|
||||
let output
|
||||
try {
|
||||
output = runPipLicenses()
|
||||
} catch (e) {
|
||||
// pip-licenses not installed — write a stub so the build doesn't fail
|
||||
// and the Settings page can show a graceful "not available" message.
|
||||
output = [
|
||||
'Third-Party Software Licenses (Python)',
|
||||
'=======================================',
|
||||
'',
|
||||
'WARNING: pip-licenses was not found in the Python environment.',
|
||||
'The Python dependency license inventory could not be generated.',
|
||||
'',
|
||||
'To generate this file, install pip-licenses in the hermes venv:',
|
||||
' pip install pip-licenses',
|
||||
'',
|
||||
`Error: ${e.message}`,
|
||||
'',
|
||||
].join('\n')
|
||||
console.warn(`[generate-python-licenses] pip-licenses not available: ${e.message}`)
|
||||
}
|
||||
|
||||
// Prepend a header so the file is self-documenting
|
||||
const header = [
|
||||
'Third-Party Software Licenses (Python)',
|
||||
'=======================================',
|
||||
'',
|
||||
'This file lists the open-source licenses of the Python dependencies',
|
||||
'of the Hermes Agent runtime bundled with the Hermes desktop app.',
|
||||
'',
|
||||
`Generated: ${generatedAt()}`,
|
||||
'',
|
||||
].join('\n')
|
||||
|
||||
const outPath = path.join(distDir, 'dependencies-python.txt')
|
||||
writeFileSync(outPath, header + output, 'utf8')
|
||||
|
||||
// Count packages: in plain-vertical format, each block is name\nversion\nlicense\ntext
|
||||
// We approximate by counting version-like lines (X.Y.Z or X.Y patterns).
|
||||
const pkgCount = (output.match(/^\d+\.\d+/gm) || []).length
|
||||
console.log(`[generate-python-licenses] Wrote ~${pkgCount} packages to ${outPath}`)
|
||||
}
|
||||
|
||||
main()
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
BarChart3,
|
||||
Bell,
|
||||
Download,
|
||||
FileText,
|
||||
Globe,
|
||||
Info,
|
||||
Keyboard,
|
||||
@@ -38,6 +39,7 @@ import { SECTIONS } from './constants'
|
||||
import { GatewaySettings } from './gateway-settings'
|
||||
import { KeybindSettings } from './keybind-settings'
|
||||
import { KEYS_VIEWS, KeysSettings, type KeysView } from './keys-settings'
|
||||
import { LicensesSettings } from './licenses-settings'
|
||||
import { NotificationsSettings } from './notifications-settings'
|
||||
import { PluginsSettings } from './plugins-settings'
|
||||
import { PROVIDER_VIEWS, ProvidersSettings, type ProviderView } from './providers-settings'
|
||||
@@ -54,7 +56,8 @@ const SETTINGS_VIEWS: readonly SettingsViewId[] = [
|
||||
'billing',
|
||||
'plugins',
|
||||
'sessions',
|
||||
'about'
|
||||
'about',
|
||||
'licenses'
|
||||
]
|
||||
|
||||
export function SettingsView({ onClose, onConfigSaved, onMainModelChanged }: SettingsPageProps) {
|
||||
@@ -249,6 +252,13 @@ export function SettingsView({ onClose, onConfigSaved, onMainModelChanged }: Set
|
||||
id: 'about',
|
||||
label: t.settings.nav.about,
|
||||
onSelect: () => setActiveView('about')
|
||||
},
|
||||
{
|
||||
active: activeView === 'licenses',
|
||||
icon: FileText,
|
||||
id: 'licenses',
|
||||
label: t.settings.nav.licenses,
|
||||
onSelect: () => setActiveView('licenses')
|
||||
}
|
||||
]
|
||||
|
||||
@@ -293,6 +303,8 @@ export function SettingsView({ onClose, onConfigSaved, onMainModelChanged }: Set
|
||||
<AppearanceSettings />
|
||||
) : activeView === 'about' ? (
|
||||
<AboutSettings />
|
||||
) : activeView === 'licenses' ? (
|
||||
<LicensesSettings />
|
||||
) : activeView === 'gateway' ? (
|
||||
<GatewaySettings />
|
||||
) : activeView === 'keybinds' ? (
|
||||
|
||||
@@ -0,0 +1,145 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Codicon } from '@/components/ui/codicon'
|
||||
import { useI18n } from '@/i18n'
|
||||
import { FileText, Loader2, AlertCircle } from '@/lib/icons'
|
||||
|
||||
import { ListRow, SectionHeading, SettingsContent } from './primitives'
|
||||
|
||||
type LicenseSource = {
|
||||
key: string
|
||||
filename: string
|
||||
label: string
|
||||
description: string
|
||||
}
|
||||
|
||||
const LICENSE_SOURCES: readonly LicenseSource[] = [
|
||||
{
|
||||
key: 'js',
|
||||
filename: 'dependencies.txt',
|
||||
label: 'JavaScript Dependencies',
|
||||
description: 'npm packages bundled with the desktop app renderer and Electron main process.'
|
||||
},
|
||||
{
|
||||
key: 'python',
|
||||
filename: 'dependencies-python.txt',
|
||||
label: 'Python Dependencies',
|
||||
description: 'Python packages from the Hermes Agent runtime environment.'
|
||||
}
|
||||
]
|
||||
|
||||
async function loadLicenseFile(filename: string): Promise<string | null> {
|
||||
const bridge = window.hermesDesktop
|
||||
if (!bridge?.readBundledLicenseFile) {
|
||||
return null
|
||||
}
|
||||
|
||||
return bridge.readBundledLicenseFile(filename)
|
||||
}
|
||||
|
||||
function LicenseFileBlock({ source }: { source: LicenseSource }) {
|
||||
const [content, setContent] = useState<string | null | undefined>(undefined)
|
||||
|
||||
useEffect(() => {
|
||||
let cancelled = false
|
||||
void loadLicenseFile(source.filename).then(result => {
|
||||
if (!cancelled) {
|
||||
setContent(result)
|
||||
}
|
||||
})
|
||||
return () => {
|
||||
cancelled = true
|
||||
}
|
||||
}, [source.filename])
|
||||
|
||||
const isLoading = content === undefined
|
||||
const isMissing = content === null
|
||||
const isEmpty = content !== null && content !== undefined && content.trim() === ''
|
||||
|
||||
return (
|
||||
<div className="mt-4">
|
||||
<SectionHeading icon={FileText} title={source.label} />
|
||||
|
||||
<div className="overflow-hidden rounded-xl border border-border/70 bg-muted/20">
|
||||
{isLoading ? (
|
||||
<div className="flex items-center gap-2 px-4 py-3 text-sm text-muted-foreground">
|
||||
<Loader2 className="size-3 animate-spin" />
|
||||
Loading…
|
||||
</div>
|
||||
) : isMissing ? (
|
||||
<div className="flex items-start gap-2 px-4 py-3 text-sm text-muted-foreground">
|
||||
<AlertCircle className="mt-0.5 size-3.5 shrink-0 text-amber-500" />
|
||||
<div>
|
||||
<p className="font-medium text-foreground">License file not found</p>
|
||||
<p className="mt-0.5 text-xs text-muted-foreground">
|
||||
Run <code className="rounded bg-muted px-1 py-0.5 text-xs">npm run generate-licenses</code> in{' '}
|
||||
<code className="rounded bg-muted px-1 py-0.5 text-xs">apps/desktop</code> to generate it, or it was
|
||||
not bundled with this build.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
) : isEmpty ? (
|
||||
<div className="px-4 py-3 text-sm text-muted-foreground">
|
||||
License file is empty.
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<div className="flex items-center justify-between border-b border-border/50 px-4 py-2">
|
||||
<span className="text-xs text-muted-foreground">
|
||||
{source.description}
|
||||
</span>
|
||||
<Button
|
||||
onClick={() => void navigator.clipboard.writeText(content || '')}
|
||||
size="sm"
|
||||
variant="text"
|
||||
>
|
||||
<Codicon className="size-3" name="copy" />
|
||||
Copy
|
||||
</Button>
|
||||
</div>
|
||||
<pre className="max-h-[400px] overflow-auto px-4 py-3 text-xs leading-relaxed text-foreground/80 whitespace-pre-wrap break-words font-mono">
|
||||
{content}
|
||||
</pre>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export function LicensesSettings() {
|
||||
return (
|
||||
<SettingsContent>
|
||||
<div className="mx-auto w-full max-w-4xl">
|
||||
<SectionHeading icon={FileText} title="Third-Party Licenses" />
|
||||
|
||||
<ListRow
|
||||
description="License attributions for open-source dependencies bundled with Hermes. These files are generated at build time and shipped with the application."
|
||||
title="Open-Source License Attribution"
|
||||
/>
|
||||
|
||||
{LICENSE_SOURCES.map(source => (
|
||||
<LicenseFileBlock key={source.key} source={source} />
|
||||
))}
|
||||
|
||||
<div className="mt-6 px-1 pb-4 text-xs text-muted-foreground">
|
||||
Hermes Agent is licensed under the MIT License. For the full text, see the LICENSE file in the{' '}
|
||||
<a
|
||||
href="https://github.com/NousResearch/hermes-agent"
|
||||
onClick={e => {
|
||||
e.preventDefault()
|
||||
void window.hermesDesktop?.openExternal?.('https://github.com/NousResearch/hermes-agent')
|
||||
}}
|
||||
className="text-primary hover:underline"
|
||||
rel="noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
source repository
|
||||
</a>
|
||||
.
|
||||
</div>
|
||||
</div>
|
||||
</SettingsContent>
|
||||
)
|
||||
}
|
||||
@@ -10,6 +10,7 @@ export type SettingsView =
|
||||
| 'gateway'
|
||||
| 'keybinds'
|
||||
| 'keys'
|
||||
| 'licenses'
|
||||
| 'notifications'
|
||||
| 'plugins'
|
||||
| 'providers'
|
||||
|
||||
Vendored
+1
@@ -76,6 +76,7 @@ declare global {
|
||||
requestMicrophoneAccess: () => Promise<boolean>
|
||||
readFileDataUrl: (filePath: string) => Promise<string>
|
||||
readFileText: (filePath: string) => Promise<HermesReadFileTextResult>
|
||||
readBundledLicenseFile: (filename: string) => Promise<string | null>
|
||||
selectPaths: (options?: HermesSelectPathsOptions) => Promise<string[]>
|
||||
writeClipboard: (text: string) => Promise<boolean>
|
||||
saveImageFromUrl: (url: string) => Promise<boolean>
|
||||
|
||||
@@ -325,7 +325,8 @@ export const en: Translations = {
|
||||
about: 'About',
|
||||
billing: 'Billing',
|
||||
notifications: 'Notifications',
|
||||
plugins: 'Plugins'
|
||||
plugins: 'Plugins',
|
||||
licenses: 'Licenses'
|
||||
},
|
||||
plugins: {
|
||||
title: 'Desktop plugins',
|
||||
|
||||
@@ -225,7 +225,8 @@ export const ja = defineLocale({
|
||||
archivedChats: 'アーカイブ済みチャット',
|
||||
about: '情報',
|
||||
billing: '請求',
|
||||
notifications: '通知'
|
||||
notifications: '通知',
|
||||
licenses: 'ライセンス'
|
||||
},
|
||||
notifications: {
|
||||
title: '通知',
|
||||
|
||||
@@ -284,6 +284,7 @@ export interface Translations {
|
||||
billing: string
|
||||
notifications: string
|
||||
plugins: string
|
||||
licenses: string
|
||||
}
|
||||
plugins: {
|
||||
title: string
|
||||
|
||||
@@ -219,7 +219,8 @@ export const zhHant = defineLocale({
|
||||
archivedChats: '已封存聊天',
|
||||
about: '關於',
|
||||
billing: '帳單',
|
||||
notifications: '通知'
|
||||
notifications: '通知',
|
||||
licenses: '授權條款'
|
||||
},
|
||||
notifications: {
|
||||
title: '通知',
|
||||
|
||||
@@ -316,7 +316,8 @@ export const zh: Translations = {
|
||||
about: '关于',
|
||||
billing: '账单',
|
||||
notifications: '通知',
|
||||
plugins: '插件'
|
||||
plugins: '插件',
|
||||
licenses: '许可证'
|
||||
},
|
||||
plugins: {
|
||||
title: '桌面插件',
|
||||
|
||||
+8
-3
@@ -14,6 +14,7 @@
|
||||
hermesNpmLib,
|
||||
electron,
|
||||
hermesAgent,
|
||||
licensePython,
|
||||
...
|
||||
}:
|
||||
let
|
||||
@@ -72,11 +73,15 @@ let
|
||||
# typecheck :3
|
||||
npm exec tsc -b
|
||||
|
||||
# build the renderer bundle
|
||||
# vite's emptyOutDir wipes dist/ on every run
|
||||
# so it has to be first
|
||||
# Build the renderer bundle first: Vite's emptyOutDir wipes dist/.
|
||||
npm exec vite build
|
||||
|
||||
# Generate attribution files AFTER Vite, otherwise emptyOutDir would
|
||||
# delete them. HERMES_PYTHON points at the sealed nix venv that has
|
||||
# hermes's locked runtime closure plus pip-licenses from [dev].
|
||||
HERMES_PYTHON=${licensePython}/bin/python3 node scripts/generate-js-licenses.mjs
|
||||
HERMES_PYTHON=${licensePython}/bin/python3 node scripts/generate-python-licenses.mjs
|
||||
|
||||
# build the electron bundle
|
||||
node scripts/bundle-electron-main.mjs
|
||||
|
||||
|
||||
@@ -225,6 +225,11 @@ stdenv.mkDerivation (finalAttrs: {
|
||||
|
||||
passthru =
|
||||
let
|
||||
# A sealed, non-editable venv containing the normal [all] runtime
|
||||
# groups plus [dev] tooling. Desktop's Nix renderer build uses this
|
||||
# only for pip-licenses, so the generated attribution inventory matches
|
||||
# the locked Python closure without making license tooling a runtime dep.
|
||||
licensePython = (mkHermesVenv (extraDependencyGroups ++ [ "dev" ])).venv;
|
||||
devPython = (mkHermesVenv (extraDependencyGroups ++ [ "dev" ])).editableVenv;
|
||||
in
|
||||
{
|
||||
@@ -233,6 +238,7 @@ stdenv.mkDerivation (finalAttrs: {
|
||||
hermesWeb
|
||||
hermesNpmLib
|
||||
hermesVenv
|
||||
licensePython
|
||||
;
|
||||
|
||||
# `hermesDesktop` references `finalAttrs.finalPackage` (this whole
|
||||
@@ -243,7 +249,7 @@ stdenv.mkDerivation (finalAttrs: {
|
||||
# runtime PATH (ripgrep/git/ffmpeg/etc). No re-implementation
|
||||
# of the agent resolution in the desktop wrapper.
|
||||
hermesDesktop = callPackage ./desktop.nix {
|
||||
inherit hermesNpmLib electron;
|
||||
inherit hermesNpmLib electron licensePython;
|
||||
hermesAgent = finalAttrs.finalPackage;
|
||||
};
|
||||
|
||||
|
||||
Generated
+441
-1
@@ -178,6 +178,7 @@
|
||||
"eslint-plugin-unused-imports": "^4.4.1",
|
||||
"globals": "^16.5.0",
|
||||
"jsdom": "^29.1.1",
|
||||
"license-checker": "^25.0.1",
|
||||
"prettier": "^3.8.3",
|
||||
"rcedit": "^5.0.2",
|
||||
"tsx": "^4.22.4",
|
||||
@@ -7368,6 +7369,16 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/array-find-index": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz",
|
||||
"integrity": "sha512-M1HQyIXcBGtVywBt8WVdim+lrNaK7VHp99Qt5pSNziXznKHViIBbXWtfRTpEFpF/c4FdfxNAsCCwPp5phBYJtw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/array-includes": {
|
||||
"version": "3.1.9",
|
||||
"resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.9.tgz",
|
||||
@@ -7489,6 +7500,13 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/asap": {
|
||||
"version": "2.0.6",
|
||||
"resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
|
||||
"integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/asn1js": {
|
||||
"version": "3.0.10",
|
||||
"resolved": "https://registry.npmjs.org/asn1js/-/asn1js-3.0.10.tgz",
|
||||
@@ -9202,6 +9220,17 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/debuglog": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/debuglog/-/debuglog-1.0.1.tgz",
|
||||
"integrity": "sha512-syBZ+rnAK3EgMsH2aYEOLUW7mZSY9Gb+0wUMCFsZvcmiz+HigA0LOcq/HoQqVuGG+EKykunc7QG2bzrponfaSw==",
|
||||
"deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/decamelize": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
|
||||
@@ -9386,6 +9415,17 @@
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/dezalgo": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz",
|
||||
"integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"asap": "^2.0.0",
|
||||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"node_modules/dijkstrajs": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.3.tgz",
|
||||
@@ -13153,6 +13193,13 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"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",
|
||||
@@ -13321,6 +13368,147 @@
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/license-checker": {
|
||||
"version": "25.0.1",
|
||||
"resolved": "https://registry.npmjs.org/license-checker/-/license-checker-25.0.1.tgz",
|
||||
"integrity": "sha512-mET5AIwl7MR2IAKYYoVBBpV0OnkKQ1xGj2IMMeEFIs42QAkEVjRtFZGWmQ28WeU7MP779iAgOaOy93Mn44mn6g==",
|
||||
"dev": true,
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"chalk": "^2.4.1",
|
||||
"debug": "^3.1.0",
|
||||
"mkdirp": "^0.5.1",
|
||||
"nopt": "^4.0.1",
|
||||
"read-installed": "~4.0.3",
|
||||
"semver": "^5.5.0",
|
||||
"spdx-correct": "^3.0.0",
|
||||
"spdx-expression-parse": "^3.0.0",
|
||||
"spdx-satisfies": "^4.0.0",
|
||||
"treeify": "^1.1.0"
|
||||
},
|
||||
"bin": {
|
||||
"license-checker": "bin/license-checker"
|
||||
}
|
||||
},
|
||||
"node_modules/license-checker/node_modules/abbrev": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
|
||||
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/license-checker/node_modules/ansi-styles": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
|
||||
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"color-convert": "^1.9.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/license-checker/node_modules/chalk": {
|
||||
"version": "2.4.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
|
||||
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-styles": "^3.2.1",
|
||||
"escape-string-regexp": "^1.0.5",
|
||||
"supports-color": "^5.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/license-checker/node_modules/color-convert": {
|
||||
"version": "1.9.3",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
|
||||
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"color-name": "1.1.3"
|
||||
}
|
||||
},
|
||||
"node_modules/license-checker/node_modules/color-name": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
|
||||
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/license-checker/node_modules/debug": {
|
||||
"version": "3.2.7",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
|
||||
"integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ms": "^2.1.1"
|
||||
}
|
||||
},
|
||||
"node_modules/license-checker/node_modules/escape-string-regexp": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
|
||||
"integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/license-checker/node_modules/has-flag": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
|
||||
"integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/license-checker/node_modules/nopt": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz",
|
||||
"integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"abbrev": "1",
|
||||
"osenv": "^0.1.4"
|
||||
},
|
||||
"bin": {
|
||||
"nopt": "bin/nopt.js"
|
||||
}
|
||||
},
|
||||
"node_modules/license-checker/node_modules/semver": {
|
||||
"version": "5.7.2",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
|
||||
"integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
"semver": "bin/semver"
|
||||
}
|
||||
},
|
||||
"node_modules/license-checker/node_modules/supports-color": {
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
|
||||
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"has-flag": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/lightningcss": {
|
||||
"version": "1.32.0",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz",
|
||||
@@ -14803,7 +14991,6 @@
|
||||
"integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"minimist": "^1.2.6"
|
||||
},
|
||||
@@ -15060,6 +15247,58 @@
|
||||
"node": "^20.17.0 || >=22.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/normalize-package-data": {
|
||||
"version": "2.5.0",
|
||||
"resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
|
||||
"integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==",
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"dependencies": {
|
||||
"hosted-git-info": "^2.1.4",
|
||||
"resolve": "^1.10.0",
|
||||
"semver": "2 || 3 || 4 || 5",
|
||||
"validate-npm-package-license": "^3.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/normalize-package-data/node_modules/hosted-git-info": {
|
||||
"version": "2.8.9",
|
||||
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
|
||||
"integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/normalize-package-data/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/normalize-package-data/node_modules/semver": {
|
||||
"version": "5.7.2",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
|
||||
"integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
"semver": "bin/semver"
|
||||
}
|
||||
},
|
||||
"node_modules/normalize-url": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz",
|
||||
@@ -15073,6 +15312,13 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/npm-normalize-package-bin": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz",
|
||||
"integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/object-assign": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||
@@ -15254,6 +15500,38 @@
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/os-homedir": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
|
||||
"integrity": "sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/os-tmpdir": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
|
||||
"integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/osenv": {
|
||||
"version": "0.1.5",
|
||||
"resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz",
|
||||
"integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==",
|
||||
"deprecated": "This package is no longer supported.",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"os-homedir": "^1.0.0",
|
||||
"os-tmpdir": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/own-keys": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz",
|
||||
@@ -16438,6 +16716,49 @@
|
||||
"read-binary-file-arch": "cli.js"
|
||||
}
|
||||
},
|
||||
"node_modules/read-installed": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/read-installed/-/read-installed-4.0.3.tgz",
|
||||
"integrity": "sha512-O03wg/IYuV/VtnK2h/KXEt9VIbMUFbk3ERG0Iu4FhLZw0EP0T9znqrYDGn6ncbEsXUFaUjiVAWXHzxwt3lhRPQ==",
|
||||
"deprecated": "This package is no longer supported.",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"debuglog": "^1.0.1",
|
||||
"read-package-json": "^2.0.0",
|
||||
"readdir-scoped-modules": "^1.0.0",
|
||||
"semver": "2 || 3 || 4 || 5",
|
||||
"slide": "~1.1.3",
|
||||
"util-extend": "^1.0.1"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"graceful-fs": "^4.1.2"
|
||||
}
|
||||
},
|
||||
"node_modules/read-installed/node_modules/semver": {
|
||||
"version": "5.7.2",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
|
||||
"integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
"semver": "bin/semver"
|
||||
}
|
||||
},
|
||||
"node_modules/read-package-json": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-2.1.2.tgz",
|
||||
"integrity": "sha512-D1KmuLQr6ZSJS0tW8hf3WGpRlwszJOXZ3E8Yd/DNRaM5d+1wVRZdHlpGBLAuovjr28LbWvjpWkBHMxpRGGjzNA==",
|
||||
"deprecated": "This package is no longer supported. Please use @npmcli/package-json instead.",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"glob": "^7.1.1",
|
||||
"json-parse-even-better-errors": "^2.3.0",
|
||||
"normalize-package-data": "^2.0.0",
|
||||
"npm-normalize-package-bin": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/readable-stream": {
|
||||
"version": "2.3.8",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
|
||||
@@ -16461,6 +16782,20 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/readdir-scoped-modules": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/readdir-scoped-modules/-/readdir-scoped-modules-1.1.0.tgz",
|
||||
"integrity": "sha512-asaikDeqAQg7JifRsZn1NJZXo9E+VwlyCfbkZhwyISinqk5zNS6266HS5kah6P0SaQKGF6SkNnZVHUzHFYxYDw==",
|
||||
"deprecated": "This functionality has been moved to @npmcli/fs",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"debuglog": "^1.0.1",
|
||||
"dezalgo": "^1.0.0",
|
||||
"graceful-fs": "^4.1.2",
|
||||
"once": "^1.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/redux": {
|
||||
"version": "4.2.1",
|
||||
"resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz",
|
||||
@@ -17412,6 +17747,16 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/slide": {
|
||||
"version": "1.1.6",
|
||||
"resolved": "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz",
|
||||
"integrity": "sha512-NwrtjCg+lZoqhFU8fOwl4ay2ei8PaqCBOUV3/ektPY9trO1yQ1oXEfmHAhKArUVUr/hOHvy5f6AdP17dCM0zMw==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/source-map": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||
@@ -17452,6 +17797,73 @@
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/spdx-compare": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/spdx-compare/-/spdx-compare-1.0.0.tgz",
|
||||
"integrity": "sha512-C1mDZOX0hnu0ep9dfmuoi03+eOdDoz2yvK79RxbcrVEG1NO1Ph35yW102DHWKN4pk80nwCgeMmSY5L25VE4D9A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"array-find-index": "^1.0.2",
|
||||
"spdx-expression-parse": "^3.0.0",
|
||||
"spdx-ranges": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/spdx-correct": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz",
|
||||
"integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"spdx-expression-parse": "^3.0.0",
|
||||
"spdx-license-ids": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/spdx-exceptions": {
|
||||
"version": "2.5.0",
|
||||
"resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz",
|
||||
"integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==",
|
||||
"dev": true,
|
||||
"license": "CC-BY-3.0"
|
||||
},
|
||||
"node_modules/spdx-expression-parse": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz",
|
||||
"integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"spdx-exceptions": "^2.1.0",
|
||||
"spdx-license-ids": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/spdx-license-ids": {
|
||||
"version": "3.0.23",
|
||||
"resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.23.tgz",
|
||||
"integrity": "sha512-CWLcCCH7VLu13TgOH+r8p1O/Znwhqv/dbb6lqWy67G+pT1kHmeD/+V36AVb/vq8QMIQwVShJ6Ssl5FPh0fuSdw==",
|
||||
"dev": true,
|
||||
"license": "CC0-1.0"
|
||||
},
|
||||
"node_modules/spdx-ranges": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/spdx-ranges/-/spdx-ranges-2.1.1.tgz",
|
||||
"integrity": "sha512-mcdpQFV7UDAgLpXEE/jOMqvK4LBoO0uTQg0uvXUewmEFhpiZx5yJSZITHB8w1ZahKdhfZqP5GPEOKLyEq5p8XA==",
|
||||
"dev": true,
|
||||
"license": "(MIT AND CC-BY-3.0)"
|
||||
},
|
||||
"node_modules/spdx-satisfies": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/spdx-satisfies/-/spdx-satisfies-4.0.1.tgz",
|
||||
"integrity": "sha512-WVzZ/cXAzoNmjCWiEluEA3BjHp5tiUmmhn9MK+X0tBbR9sOqtC6UQwmgCNrAIZvNlMuBUYAaHYfb2oqlF9SwKA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"spdx-compare": "^1.0.0",
|
||||
"spdx-expression-parse": "^3.0.0",
|
||||
"spdx-ranges": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/split-string": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz",
|
||||
@@ -18152,6 +18564,16 @@
|
||||
"tree-kill": "cli.js"
|
||||
}
|
||||
},
|
||||
"node_modules/treeify": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/treeify/-/treeify-1.1.0.tgz",
|
||||
"integrity": "sha512-1m4RA7xVAJrSGrrXGs0L3YTwyvBs2S8PbRHaLZAkFw7JR8oIFwYtysxlBZhYIa7xSyiYJKZ3iGrrk55cGA3i9A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/trim-lines": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz",
|
||||
@@ -18803,6 +19225,13 @@
|
||||
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/util-extend": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/util-extend/-/util-extend-1.0.3.tgz",
|
||||
"integrity": "sha512-mLs5zAK+ctllYBj+iAQvlDCwoxU/WDOUaJkcFudeiAX6OajC6BKXJUa9a+tbtkC11dz2Ufb7h0lyvIOVn4LADA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/uuid": {
|
||||
"version": "14.0.1",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-14.0.1.tgz",
|
||||
@@ -18822,6 +19251,17 @@
|
||||
"integrity": "sha512-LdabyT4OffkyXFCe9UT+uMkxNBs5rcTVuZClvxQr08D5TUgo1OFKkoT65qYRCsiKBl/usHjpXvP4hHMzzDRj3A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/validate-npm-package-license": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
|
||||
"integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"spdx-correct": "^3.0.0",
|
||||
"spdx-expression-parse": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/vfile": {
|
||||
"version": "6.0.3",
|
||||
"resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz",
|
||||
|
||||
+1
-1
@@ -157,7 +157,7 @@ edge-tts = ["edge-tts==7.2.7"]
|
||||
modal = ["modal==1.3.4"]
|
||||
daytona = ["daytona==0.155.0"]
|
||||
hindsight = ["hindsight-client==0.6.1"]
|
||||
dev = ["debugpy==1.8.20", "pytest==9.0.2", "pytest-asyncio==1.3.0", "mcp==1.26.0", "starlette==1.0.1", "ty==0.0.21", "ruff==0.15.10", "setuptools==81.0.0"] # starlette: CVE-2026-48710; setuptools: latest <82 (torch >=2.11 caps setuptools<82)
|
||||
dev = ["debugpy==1.8.20", "pytest==9.0.2", "pytest-asyncio==1.3.0", "mcp==1.26.0", "starlette==1.0.1", "ty==0.0.21", "ruff==0.15.10", "setuptools==81.0.0", "pip-licenses==5.0.0"] # starlette: CVE-2026-48710; setuptools: latest <82 (torch >=2.11 caps setuptools<82); pip-licenses: generate THIRD_PARTY_LICENSES at desktop build time
|
||||
messaging = ["python-telegram-bot[webhooks]==22.6", "discord.py[voice]==2.7.1", "aiohttp==3.14.1", "brotlicffi==1.2.0.1", "slack-bolt==1.29.0", "slack-sdk==3.43.0", "qrcode==7.4.2"] # aiohttp 3.14.1: CVE-2026-34513/34518/34519/34520/34525 + 34993(RCE)/47265
|
||||
cron = [] # croniter is now a core dependency; this extra kept for back-compat
|
||||
slack = ["slack-bolt==1.29.0", "slack-sdk==3.43.0", "aiohttp==3.14.1"]
|
||||
|
||||
@@ -1591,6 +1591,7 @@ daytona = [
|
||||
dev = [
|
||||
{ name = "debugpy" },
|
||||
{ name = "mcp" },
|
||||
{ name = "pip-licenses" },
|
||||
{ name = "pytest" },
|
||||
{ name = "pytest-asyncio" },
|
||||
{ name = "ruff" },
|
||||
@@ -1811,6 +1812,7 @@ requires-dist = [
|
||||
{ name = "parallel-web", marker = "extra == 'parallel-web'", specifier = "==0.4.2" },
|
||||
{ name = "pathspec", specifier = "==1.1.1" },
|
||||
{ name = "pillow", specifier = "==12.2.0" },
|
||||
{ name = "pip-licenses", marker = "extra == 'dev'", specifier = "==5.0.0" },
|
||||
{ name = "prompt-toolkit", specifier = "==3.0.52" },
|
||||
{ name = "psutil", specifier = "==7.2.2" },
|
||||
{ name = "ptyprocess", marker = "sys_platform != 'win32'", specifier = ">=0.7.0,<1" },
|
||||
@@ -3013,6 +3015,19 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/bc/60/5382c03e1970de634027cee8e1b7d39776b778b81812aaf45b694dfe9e28/pillow-12.2.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:bfa9c230d2fe991bed5318a5f119bd6780cda2915cca595393649fc118ab895e", size = 7080946, upload-time = "2026-04-01T14:46:11.734Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pip-licenses"
|
||||
version = "5.0.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "prettytable" },
|
||||
{ name = "tomli" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/a0/49/d36a3ddb73d22970a35afa3e9fd53c8318150f8122e4257ca9875f1d4e38/pip_licenses-5.0.0.tar.gz", hash = "sha256:0633a1f9aab58e5a6216931b0e1d5cdded8bcc2709ff563674eb0e2ff9e77e8e", size = 41542, upload-time = "2024-07-23T10:48:29.785Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/27/0a/bfaf1479d09d19f503a669d9c8e433ac59ae687fb8da1d8207eb85c5a9f4/pip_licenses-5.0.0-py3-none-any.whl", hash = "sha256:82c83666753efb86d1af1c405c8ab273413eb10d6689c218df2f09acf40e477d", size = 20497, upload-time = "2024-07-23T10:48:27.59Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pluggy"
|
||||
version = "1.6.0"
|
||||
@@ -3049,6 +3064,18 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/c3/07/ca761fd0e3b23e9bfeaaeeffa5df4c235fd0c5086d89934553583e6902f3/posthog-7.21.0-py3-none-any.whl", hash = "sha256:11dca1e9772bedcb6721751fb0945fa8dcddbf9a4e72e2430483b53cdb8d1ec0", size = 370932, upload-time = "2026-06-26T15:56:59.844Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prettytable"
|
||||
version = "3.18.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "wcwidth" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/81/74/ba08d81e668ccfe8658d7520a307e63c19862c08eb4ccb26f356c5239a7a/prettytable-3.18.0.tar.gz", hash = "sha256:439217116152244369caf3d9f1caf2f9fe29b03bd79e88d2928c8e718c95d680", size = 76373, upload-time = "2026-06-22T16:07:50.174Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/fe/be/2e6798ace5cc036f5d05d36b7b2fd85346f1a708c87060890b070d0ec607/prettytable-3.18.0-py3-none-any.whl", hash = "sha256:b3346e0e6f79180833aebaac088ae926340586cf6d7d991b9eb125b65f72313a", size = 37357, upload-time = "2026-06-22T16:07:48.595Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prompt-toolkit"
|
||||
version = "3.0.52"
|
||||
@@ -4105,6 +4132,42 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", size = 16588, upload-time = "2020-11-01T01:40:20.672Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tomli"
|
||||
version = "2.4.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/22/de/48c59722572767841493b26183a0d1cc411d54fd759c5607c4590b6563a6/tomli-2.4.1.tar.gz", hash = "sha256:7c7e1a961a0b2f2472c1ac5b69affa0ae1132c39adcb67aba98568702b9cc23f", size = 17543, upload-time = "2026-03-25T20:22:03.828Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/f4/11/db3d5885d8528263d8adc260bb2d28ebf1270b96e98f0e0268d32b8d9900/tomli-2.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f8f0fc26ec2cc2b965b7a3b87cd19c5c6b8c5e5f436b984e85f486d652285c30", size = 154704, upload-time = "2026-03-25T20:21:10.473Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6d/f7/675db52c7e46064a9aa928885a9b20f4124ecb9bc2e1ce74c9106648d202/tomli-2.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4ab97e64ccda8756376892c53a72bd1f964e519c77236368527f758fbc36a53a", size = 149454, upload-time = "2026-03-25T20:21:12.036Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/61/71/81c50943cf953efa35bce7646caab3cf457a7d8c030b27cfb40d7235f9ee/tomli-2.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96481a5786729fd470164b47cdb3e0e58062a496f455ee41b4403be77cb5a076", size = 237561, upload-time = "2026-03-25T20:21:13.098Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/48/c1/f41d9cb618acccca7df82aaf682f9b49013c9397212cb9f53219e3abac37/tomli-2.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a881ab208c0baf688221f8cecc5401bd291d67e38a1ac884d6736cbcd8247e9", size = 243824, upload-time = "2026-03-25T20:21:14.569Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/22/e4/5a816ecdd1f8ca51fb756ef684b90f2780afc52fc67f987e3c61d800a46d/tomli-2.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:47149d5bd38761ac8be13a84864bf0b7b70bc051806bc3669ab1cbc56216b23c", size = 242227, upload-time = "2026-03-25T20:21:15.712Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6b/49/2b2a0ef529aa6eec245d25f0c703e020a73955ad7edf73e7f54ddc608aa5/tomli-2.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ec9bfaf3ad2df51ace80688143a6a4ebc09a248f6ff781a9945e51937008fcbc", size = 247859, upload-time = "2026-03-25T20:21:17.001Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/83/bd/6c1a630eaca337e1e78c5903104f831bda934c426f9231429396ce3c3467/tomli-2.4.1-cp311-cp311-win32.whl", hash = "sha256:ff2983983d34813c1aeb0fa89091e76c3a22889ee83ab27c5eeb45100560c049", size = 97204, upload-time = "2026-03-25T20:21:18.079Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/42/59/71461df1a885647e10b6bb7802d0b8e66480c61f3f43079e0dcd315b3954/tomli-2.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:5ee18d9ebdb417e384b58fe414e8d6af9f4e7a0ae761519fb50f721de398dd4e", size = 108084, upload-time = "2026-03-25T20:21:18.978Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b8/83/dceca96142499c069475b790e7913b1044c1a4337e700751f48ed723f883/tomli-2.4.1-cp311-cp311-win_arm64.whl", hash = "sha256:c2541745709bad0264b7d4705ad453b76ccd191e64aa6f0fc66b69a293a45ece", size = 95285, upload-time = "2026-03-25T20:21:20.309Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c1/ba/42f134a3fe2b370f555f44b1d72feebb94debcab01676bf918d0cb70e9aa/tomli-2.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c742f741d58a28940ce01d58f0ab2ea3ced8b12402f162f4d534dfe18ba1cd6a", size = 155924, upload-time = "2026-03-25T20:21:21.626Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/dc/c7/62d7a17c26487ade21c5422b646110f2162f1fcc95980ef7f63e73c68f14/tomli-2.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7f86fd587c4ed9dd76f318225e7d9b29cfc5a9d43de44e5754db8d1128487085", size = 150018, upload-time = "2026-03-25T20:21:23.002Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5c/05/79d13d7c15f13bdef410bdd49a6485b1c37d28968314eabee452c22a7fda/tomli-2.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ff18e6a727ee0ab0388507b89d1bc6a22b138d1e2fa56d1ad494586d61d2eae9", size = 244948, upload-time = "2026-03-25T20:21:24.04Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/10/90/d62ce007a1c80d0b2c93e02cab211224756240884751b94ca72df8a875ca/tomli-2.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:136443dbd7e1dee43c68ac2694fde36b2849865fa258d39bf822c10e8068eac5", size = 253341, upload-time = "2026-03-25T20:21:25.177Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1a/7e/caf6496d60152ad4ed09282c1885cca4eea150bfd007da84aea07bcc0a3e/tomli-2.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5e262d41726bc187e69af7825504c933b6794dc3fbd5945e41a79bb14c31f585", size = 248159, upload-time = "2026-03-25T20:21:26.364Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/99/e7/c6f69c3120de34bbd882c6fba7975f3d7a746e9218e56ab46a1bc4b42552/tomli-2.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5cb41aa38891e073ee49d55fbc7839cfdb2bc0e600add13874d048c94aadddd1", size = 253290, upload-time = "2026-03-25T20:21:27.46Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d6/2f/4a3c322f22c5c66c4b836ec58211641a4067364f5dcdd7b974b4c5da300c/tomli-2.4.1-cp312-cp312-win32.whl", hash = "sha256:da25dc3563bff5965356133435b757a795a17b17d01dbc0f42fb32447ddfd917", size = 98141, upload-time = "2026-03-25T20:21:28.492Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/24/22/4daacd05391b92c55759d55eaee21e1dfaea86ce5c571f10083360adf534/tomli-2.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:52c8ef851d9a240f11a88c003eacb03c31fc1c9c4ec64a99a0f922b93874fda9", size = 108847, upload-time = "2026-03-25T20:21:29.386Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/68/fd/70e768887666ddd9e9f5d85129e84910f2db2796f9096aa02b721a53098d/tomli-2.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:f758f1b9299d059cc3f6546ae2af89670cb1c4d48ea29c3cacc4fe7de3058257", size = 95088, upload-time = "2026-03-25T20:21:30.677Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/07/06/b823a7e818c756d9a7123ba2cda7d07bc2dd32835648d1a7b7b7a05d848d/tomli-2.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:36d2bd2ad5fb9eaddba5226aa02c8ec3fa4f192631e347b3ed28186d43be6b54", size = 155866, upload-time = "2026-03-25T20:21:31.65Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/14/6f/12645cf7f08e1a20c7eb8c297c6f11d31c1b50f316a7e7e1e1de6e2e7b7e/tomli-2.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:eb0dc4e38e6a1fd579e5d50369aa2e10acfc9cace504579b2faabb478e76941a", size = 149887, upload-time = "2026-03-25T20:21:33.028Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5c/e0/90637574e5e7212c09099c67ad349b04ec4d6020324539297b634a0192b0/tomli-2.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c7f2c7f2b9ca6bdeef8f0fa897f8e05085923eb091721675170254cbc5b02897", size = 243704, upload-time = "2026-03-25T20:21:34.51Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/10/8f/d3ddb16c5a4befdf31a23307f72828686ab2096f068eaf56631e136c1fdd/tomli-2.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f3c6818a1a86dd6dca7ddcaaf76947d5ba31aecc28cb1b67009a5877c9a64f3f", size = 251628, upload-time = "2026-03-25T20:21:36.012Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e3/f1/dbeeb9116715abee2485bf0a12d07a8f31af94d71608c171c45f64c0469d/tomli-2.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d312ef37c91508b0ab2cee7da26ec0b3ed2f03ce12bd87a588d771ae15dcf82d", size = 247180, upload-time = "2026-03-25T20:21:37.136Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d3/74/16336ffd19ed4da28a70959f92f506233bd7cfc2332b20bdb01591e8b1d1/tomli-2.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:51529d40e3ca50046d7606fa99ce3956a617f9b36380da3b7f0dd3dd28e68cb5", size = 251674, upload-time = "2026-03-25T20:21:38.298Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/16/f9/229fa3434c590ddf6c0aa9af64d3af4b752540686cace29e6281e3458469/tomli-2.4.1-cp313-cp313-win32.whl", hash = "sha256:2190f2e9dd7508d2a90ded5ed369255980a1bcdd58e52f7fe24b8162bf9fedbd", size = 97976, upload-time = "2026-03-25T20:21:39.316Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6a/1e/71dfd96bcc1c775420cb8befe7a9d35f2e5b1309798f009dca17b7708c1e/tomli-2.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:8d65a2fbf9d2f8352685bc1364177ee3923d6baf5e7f43ea4959d7d8bc326a36", size = 108755, upload-time = "2026-03-25T20:21:40.248Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/83/7a/d34f422a021d62420b78f5c538e5b102f62bea616d1d75a13f0a88acb04a/tomli-2.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:4b605484e43cdc43f0954ddae319fb75f04cc10dd80d830540060ee7cd0243cd", size = 95265, upload-time = "2026-03-25T20:21:41.219Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7b/61/cceae43728b7de99d9b847560c262873a1f6c98202171fd5ed62640b494b/tomli-2.4.1-py3-none-any.whl", hash = "sha256:0d85819802132122da43cb86656f8d1f8c6587d54ae7dcaf30e90533028b49fe", size = 14583, upload-time = "2026-03-25T20:22:03.012Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tornado"
|
||||
version = "6.5.5"
|
||||
|
||||
Reference in New Issue
Block a user