interlatch 0.29.2__tar.gz → 0.29.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {interlatch-0.29.2 → interlatch-0.29.4}/.pre-commit-config.yaml +2 -2
- {interlatch-0.29.2 → interlatch-0.29.4}/CHANGELOG.md +20 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/PKG-INFO +1 -1
- interlatch-0.29.4/changelog.d/browser-extension.md +4 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/docs/configuration.md +1 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/docs/ja/configuration.md +1 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/docs/ja/privacy.md +2 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/docs/ja/sources.md +33 -4
- {interlatch-0.29.2 → interlatch-0.29.4}/docs/privacy.md +2 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/docs/sources.md +38 -4
- {interlatch-0.29.2 → interlatch-0.29.4}/packaging/macos/Interlatch.spec +1 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/analysis/queue.py +24 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/application/workflows.py +10 -5
- interlatch-0.29.4/src/interlatch/browser_extension/background.js +58 -0
- interlatch-0.29.4/src/interlatch/browser_extension/capture.js +202 -0
- interlatch-0.29.4/src/interlatch/browser_extension/icon-128.png +0 -0
- interlatch-0.29.4/src/interlatch/browser_extension/icon-16.png +0 -0
- interlatch-0.29.4/src/interlatch/browser_extension/icon-32.png +0 -0
- interlatch-0.29.4/src/interlatch/browser_extension/icon-48.png +0 -0
- interlatch-0.29.4/src/interlatch/browser_extension/manifest.json +18 -0
- interlatch-0.29.4/src/interlatch/browser_extension/popup.css +42 -0
- interlatch-0.29.4/src/interlatch/browser_extension/popup.html +33 -0
- interlatch-0.29.4/src/interlatch/browser_extension/popup.js +81 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/core/config.py +4 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/core/i18n/ja.py +5 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/core/sessions.py +4 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/ingestion/chat_import.py +96 -30
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/interfaces/web/api/sources.py +24 -3
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/interfaces/web/api/status.py +50 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/interfaces/web/routes.py +4 -1
- interlatch-0.29.4/src/interlatch/runtime/browser_extension.py +43 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/sources/chatgpt.py +3 -1
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/sources/chats.py +4 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/sources/claude/export.py +6 -4
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/web/app.css +40 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/web/app.js +177 -10
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/web/index.html +2 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/web/ja.js +70 -2
- interlatch-0.29.4/tests/test_browser_extension.py +41 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_chat_import.py +129 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_party.py +5 -5
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_queue.py +60 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_route_matrix.py +2 -0
- interlatch-0.29.2/changelog.d/hub-glossary-members.md +0 -4
- interlatch-0.29.2/changelog.d/hub-project-page.md +0 -6
- {interlatch-0.29.2 → interlatch-0.29.4}/.github/dependabot.yml +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/.github/windows-ja-qa/driver.ps1 +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/.github/windows-ja-qa/ja_check.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/.github/windows-ja-qa/report.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/.github/windows-ja-qa/shots.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/.github/windows-ja-qa/user.ps1 +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/.github/workflows/changelog.yml +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/.github/workflows/ci.yml +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/.github/workflows/docs.yml +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/.github/workflows/release.yml +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/.github/workflows/windows-ja-qa.yml +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/.gitignore +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/.python-version +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/CONTRIBUTING.md +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/LICENSE +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/README.ja.md +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/README.md +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/ROADMAP.ja.md +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/ROADMAP.md +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/SECURITY.md +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/careers/README.md +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/careers/boards.json +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/careers/pipeline.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/changelog.d/README.md +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/dev.sh +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/docker/.dockerignore +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/docker/.env.example +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/docker/Caddyfile +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/docker/Dockerfile +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/docker/compose.yaml +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/docker/wheels/.gitignore +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/docs/README.md +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/docs/_site/hooks.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/docs/analysis.md +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/docs/cli.md +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/docs/dashboard.md +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/docs/demo/make_demo.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/docs/demo/record_demo.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/docs/design/innerlatch.md +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/docs/design/ja/innerlatch.md +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/docs/design/ja/learning.md +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/docs/design/learning.md +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/docs/development.md +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/docs/devices.md +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/docs/diagrams/analysis.excalidraw.svg +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/docs/diagrams/architecture.excalidraw.svg +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/docs/docker.md +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/docs/faq.md +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/docs/images/demo.gif +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/docs/images/group-connections.png +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/docs/images/hero.png +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/docs/images/home.png +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/docs/images/job-goal.png +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/docs/images/map.png +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/docs/images/palette.png +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/docs/images/session.png +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/docs/install.md +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/docs/ja/README.md +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/docs/ja/analysis.md +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/docs/ja/cli.md +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/docs/ja/dashboard.md +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/docs/ja/development.md +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/docs/ja/devices.md +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/docs/ja/docker.md +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/docs/ja/faq.md +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/docs/ja/install.md +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/docs/ja/join-a-hub.md +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/docs/ja/mcp.md +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/docs/ja/moving-from-chronicle.md +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/docs/ja/party.md +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/docs/ja/postgres.md +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/docs/ja/profiles.md +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/docs/ja/suggestions.md +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/docs/ja/troubleshooting.md +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/docs/ja/vscode.md +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/docs/join-a-hub.md +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/docs/mcp.md +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/docs/moving-from-chronicle.md +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/docs/party.md +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/docs/postgres.md +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/docs/profiles.md +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/docs/suggestions.md +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/docs/troubleshooting.md +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/docs/vscode.md +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/leaderboard/.firebaserc +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/leaderboard/.gitignore +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/leaderboard/README.md +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/leaderboard/firebase.json +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/leaderboard/firestore.indexes.json +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/leaderboard/firestore.rules +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/mkdocs.yml +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/packaging/icons3d/README.md +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/packaging/icons3d/art.json +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/packaging/icons3d/filing.html +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/packaging/icons3d/loops.json +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/packaging/icons3d/loops.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/packaging/icons3d/members.js +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/packaging/icons3d/render.html +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/packaging/icons3d/render.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/packaging/icons3d/scenes-more.js +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/packaging/icons3d/scenes.html +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/packaging/macos/Chronicle.icns +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/packaging/macos/build.sh +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/packaging/macos/entitlements.plist +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/packaging/macos/icon-3d.webp +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/packaging/macos/icon-blueprint-prompt.md +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/packaging/macos/icon-blueprint.webp +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/packaging/macos/icon.png +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/packaging/macos/interlatch_app.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/packaging/macos/make_icon.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/packaging/release.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/pyproject.toml +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/chronicle/__init__.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/chronicle/__main__.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/chronicle/windows.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/__init__.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/__main__.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/analysis/__init__.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/analysis/digest.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/analysis/language.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/analysis/runners/__init__.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/analysis/runners/api.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/analysis/runners/base.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/analysis/runners/bob.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/analysis/runners/claude.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/analysis/runners/codex.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/analysis/runners/factory.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/analysis/runners/off.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/analysis/runners/opencode.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/analysis/runners/process.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/analysis/runners/replies.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/analysis/runners/settings.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/analysis/screen.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/analysis/session.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/analysis/worker.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/application/__init__.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/application/profiles.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/application/records.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/application/search.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/artifacts/__init__.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/artifacts/files.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/artifacts/queries.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/cli.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/core/__init__.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/core/agents.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/core/artifact_records.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/core/budget.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/core/diagrams.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/core/i18n/__init__.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/core/machine.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/core/paths.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/core/pricing.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/core/process.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/core/profiles.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/core/redact.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/core/util.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/ingestion/__init__.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/ingestion/analyses.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/ingestion/archive.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/ingestion/deletion.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/ingestion/history.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/ingestion/memory.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/ingestion/persistence.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/ingestion/report.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/ingestion/service.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/interfaces/__init__.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/interfaces/cli/__init__.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/interfaces/cli/commands/__init__.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/interfaces/cli/commands/archive.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/interfaces/cli/commands/hubs.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/interfaces/cli/commands/knowledge.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/interfaces/cli/commands/operations.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/interfaces/cli/commands/recording.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/interfaces/cli/commands/setup.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/interfaces/cli/commands/sharing.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/interfaces/cli/common.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/interfaces/cli/main.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/interfaces/cli/parser.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/interfaces/exports/__init__.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/interfaces/exports/diagrams.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/interfaces/exports/markdown.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/interfaces/exports/sessions.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/interfaces/exports/vault.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/interfaces/mcp.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/interfaces/web/__init__.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/interfaces/web/api/__init__.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/interfaces/web/api/artifacts.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/interfaces/web/api/common.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/interfaces/web/api/devices.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/interfaces/web/api/hub_admin.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/interfaces/web/api/hubs.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/interfaces/web/api/knowledge.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/interfaces/web/api/leaderboard.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/interfaces/web/api/party.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/interfaces/web/api/projects.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/interfaces/web/api/sessions.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/interfaces/web/api/settings.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/interfaces/web/api/storage.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/interfaces/web/api/team.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/interfaces/web/app.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/interfaces/web/assets.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/interfaces/web/context.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/interfaces/web/handler.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/interfaces/web/jobs.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/interfaces/web/server.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/knowledge/__init__.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/knowledge/arena.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/knowledge/bag.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/knowledge/cases.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/knowledge/formation.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/knowledge/freshness.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/knowledge/friction.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/knowledge/gates.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/knowledge/glossary.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/knowledge/group_graph.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/knowledge/hero.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/knowledge/instructions.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/knowledge/interview.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/knowledge/jobprep.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/knowledge/jobs.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/knowledge/ladder.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/knowledge/learning.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/knowledge/market.json +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/knowledge/party.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/knowledge/reviews.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/knowledge/roadmap.json +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/knowledge/study.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/knowledge/suggest.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/knowledge/synthesize.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/knowledge/weekly.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/projects/__init__.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/projects/groups.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/projects/hubgroups.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/projects/labels.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/projects/owners.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/projects/progress.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/projects/resume.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/projects/routing.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/projects/scope.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/projects/systems.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/projects/team.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/runtime/__init__.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/runtime/connectors.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/runtime/container.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/runtime/feedback.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/runtime/hooks.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/runtime/install.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/runtime/migrate.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/runtime/notify.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/runtime/platforms/__init__.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/runtime/platforms/desktop.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/runtime/platforms/menubar.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/runtime/platforms/windows.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/runtime/statusline.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/runtime/tailnet.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/runtime/update.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/sharing/__init__.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/sharing/access.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/sharing/arena.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/sharing/client.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/sharing/identity.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/sharing/leaderboard.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/sharing/market.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/sharing/membership.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/sharing/news.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/sharing/people.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/sharing/projects.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/sharing/protocol.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/sharing/pull.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/sharing/push.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/sharing/receiver.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/sources/__init__.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/sources/antigravity.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/sources/bob.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/sources/builder.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/sources/claude/__init__.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/sources/claude/files.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/sources/claude/parser.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/sources/codex/__init__.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/sources/codex/cloud.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/sources/codex/parser.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/sources/copilot.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/sources/discovery.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/sources/opencode.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/sources/prompts.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/storage/__init__.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/storage/fts.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/storage/kv.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/storage/migrations.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/storage/mirror.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/storage/postgres.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/storage/schema.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/storage/sqlite.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/storage/team.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/web/AGENT-ICONS.txt +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/web/PLEX-OFL.txt +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/web/adventure.js +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/web/agent-antigravity.svg +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/web/agent-bob.svg +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/web/agent-chatgpt.svg +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/web/agent-claude-ai.svg +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/web/agent-claude.svg +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/web/agent-codex.svg +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/web/agent-copilot.svg +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/web/agent-opencode.svg +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/web/art-book.webp +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/web/art-camera.webp +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/web/art-chip.webp +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/web/art-clock.webp +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/web/art-cloud.webp +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/web/art-coins.webp +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/web/art-container.webp +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/web/art-dashboard.webp +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/web/art-dex.webp +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/web/art-document.webp +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/web/art-flag.webp +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/web/art-folder.webp +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/web/art-gateway.webp +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/web/art-gears.webp +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/web/art-glitch.webp +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/web/art-globe.webp +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/web/art-laptop.webp +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/web/art-log.webp +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/web/art-lumi.webp +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/web/art-magnifier.webp +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/web/art-pip.webp +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/web/art-plug.webp +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/web/art-rack.webp +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/web/art-tink.webp +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/web/battle.js +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/web/boot.js +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/web/favicon.png +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/web/field.js +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/web/i18n.js +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/web/icon-180.png +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/web/icon-192.png +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/web/icon-512.png +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/web/icon.png +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/web/innerlatch.css +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/web/innerlatch.js +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/web/learning.js +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/web/manifest.webmanifest +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/web/panel.css +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/web/panel.html +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/web/panel.js +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/web/plex-mono-400.woff2 +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/web/plex-mono-500.woff2 +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/web/plex-mono-600.woff2 +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/web/plex-sans-400.woff2 +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/web/plex-sans-500.woff2 +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/web/plex-sans-600.woff2 +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/web/plex-sans-700.woff2 +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/web/rough.js +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/web/scene.js +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/web/signin.js +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/web/sprites.js +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/src/interlatch/windows.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/antigravity_fixture.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/codex_fixture.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/conftest.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/copilot_fixture.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/fixtures/db/0.21.3/chronicle.db +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/fixtures/db/0.21.3/expected.json +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/fixtures/db/0.27.8/chronicle.db +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/fixtures/db/0.27.8/expected.json +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/fixtures/db/0.5.0/chronicle.db +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/fixtures/db/0.5.0/expected.json +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/fixtures/db/README.md +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/fixtures/db/make_fixture.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/friction_fixture.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/golden.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/goldens/cli.json +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/goldens/diagram.json +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/goldens/parsers/antigravity.json +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/goldens/parsers/bob.json +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/goldens/parsers/claude_code.json +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/goldens/parsers/codex.json +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/goldens/parsers/copilot.json +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/goldens/parsers/opencode.json +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/goldens/systems.json +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/hubs_fixture.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/opencode_fixture.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_adventure_web.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_analysis.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_antigravity.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_architecture.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_arena.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_artifacts.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_bag.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_battle_web.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_bob_analysis.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_boot_web.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_careers.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_case_files.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_cases.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_cli_pins.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_codex.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_codex_analysis.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_codex_cloud.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_container.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_copilot_bob.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_db_compat.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_dedicated_hub.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_dedupe.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_desktop.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_diagram.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_doctor_agents.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_ee_boundary.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_feedback.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_field_web.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_fix_effect.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_formation.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_friction.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_game_mode.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_gates.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_glossary.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_group_graph.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_group_sharing.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_guidance.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_hero.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_home_agents.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_hub.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_hub_accept.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_hub_cookie.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_hub_groups.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_hub_inventory_scope.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_hub_leave_join.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_hub_news.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_hub_payload_privacy.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_hub_projects.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_hub_projects_listed.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_hub_purge.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_hub_send_problems.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_hub_sharing_race.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_i18n_web.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_ingest.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_instructions.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_integration.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_interview.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_jobprep.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_jobs.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_knowledge_freshness.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_ladder.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_language.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_leaderboard.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_learning_glance.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_learning_interests.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_learning_web.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_leave_project.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_legacy_package.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_map_goldens.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_market.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_mcp_resume.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_menubar.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_migrate.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_mirror.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_notify.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_opencode.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_opencode_analysis.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_parser.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_parser_goldens.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_party_map.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_people.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_people_hub.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_people_web.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_profile_hubs.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_profile_scope.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_profiles.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_project_groups.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_project_progress.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_project_team.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_providers.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_regressions.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_release.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_roadmap_catalog.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_screen.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_security.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_session_context_budget.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_setup.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_statusline.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_study.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_suggest.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_suggest_cli.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_systems.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_team.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_team_home.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_update.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_weekly_reanalysis.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/test_windows.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/tests/windows_smoke.py +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/uv.lock +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/vscode-extension/.vscodeignore +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/vscode-extension/LICENSE +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/vscode-extension/README.md +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/vscode-extension/extension.js +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/vscode-extension/media/icon.png +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/vscode-extension/media/interlatch.svg +0 -0
- {interlatch-0.29.2 → interlatch-0.29.4}/vscode-extension/package.json +0 -0
|
@@ -42,7 +42,7 @@ repos:
|
|
|
42
42
|
- repo: local
|
|
43
43
|
hooks:
|
|
44
44
|
- id: node-check
|
|
45
|
-
name: node --check (dashboard
|
|
45
|
+
name: node --check (dashboard, VS Code and browser extension JavaScript parses)
|
|
46
46
|
language: system
|
|
47
47
|
entry: node --check
|
|
48
|
-
files: ^(src/interlatch/web|vscode-extension)/.*\.js$
|
|
48
|
+
files: ^(src/interlatch/web|src/interlatch/browser_extension|vscode-extension)/.*\.js$
|
|
@@ -3,6 +3,26 @@
|
|
|
3
3
|
<!-- Don't add lines here: a pull request puts its line in a new file in changelog.d/ (see changelog.d/README.md),
|
|
4
4
|
and each release moves the files it took into a dated section below. -->
|
|
5
5
|
|
|
6
|
+
## 0.29.3 (2026-10-29)
|
|
7
|
+
|
|
8
|
+
- **Analysis usage on every page:** the status bar shows your Claude plan's session (5hr) and weekly (7 day) usage, as
|
|
9
|
+
Claude Code names them, and what analysis spent today. Click it to see what's left and when each resets, this month's
|
|
10
|
+
and all-time spend, and to set the analysis limits: sessions per run, the budget per call, and a new cap that pauses
|
|
11
|
+
automatic analysis at a percent of your plan limit ([Configuration](docs/configuration.md)).
|
|
12
|
+
|
|
13
|
+
## 0.29.2 (2026-10-29)
|
|
14
|
+
|
|
15
|
+
- **A project's page on a team hub:** the team's knowledge leads. Each knowledge base entry and lesson names whose
|
|
16
|
+
sessions taught it, lessons two people found separately sit side by side, **Since your last visit** lists new and
|
|
17
|
+
replaced lessons, **Start here** greets someone new to the project, **People** shows who has worked on what (never
|
|
18
|
+
a count per person), and **Who's working on it** draws each person's sessions over eight weeks. Tokens and cost
|
|
19
|
+
move to an admin-only Usage tab, and the Systems map is no longer offered where it would be empty or refused
|
|
20
|
+
([On a team hub](docs/dashboard.md#on-a-team-hub)).
|
|
21
|
+
- **A project's glossary and mindmap on a team hub:** a project's page there has a **Glossary & map** tab: its terms
|
|
22
|
+
as a list or a mindmap. Teammates limited to some projects now see it too, with the terms their projects use and
|
|
23
|
+
what their projects say of them, never what another project wrote
|
|
24
|
+
([On a team hub](docs/dashboard.md#on-a-team-hub)).
|
|
25
|
+
|
|
6
26
|
## 0.29.1 (2026-10-25)
|
|
7
27
|
|
|
8
28
|
- **The interview, your job goal's final boss:** three questions a day, written from the job description and from
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: interlatch
|
|
3
|
-
Version: 0.29.
|
|
3
|
+
Version: 0.29.4
|
|
4
4
|
Summary: Shared memory for your coding agents: records every session (Claude Code, Codex, Copilot, Bob, Antigravity, OpenCode) locally and keeps what they learned, each lesson linked to the session it came from.
|
|
5
5
|
Project-URL: Homepage, https://interlatch.com
|
|
6
6
|
Project-URL: Documentation, https://interlatch.com/docs/
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
- **claude.ai and ChatGPT chats, saved as you use the sites:** a browser extension for Chrome, Edge and Brave sends
|
|
2
|
+
your chats to the dashboard while a claude.ai or chatgpt.com tab is open, so new chats need no new export. Chats in
|
|
3
|
+
ChatGPT projects keep their project's name. **Settings › Browser extension** walks you through installing it
|
|
4
|
+
([Saving chats as you use the sites](docs/sources.md#saving-chats-as-you-use-the-sites)).
|
|
@@ -35,6 +35,7 @@ the file. Changes apply on the next sync or worker run. `INTERLATCH_HOME` reloca
|
|
|
35
35
|
| `idle_minutes` | `20` | a session must have ended or been idle this long before it is analyzed |
|
|
36
36
|
| `min_prompts` | `1` | sessions with fewer human prompts are skipped |
|
|
37
37
|
| `max_per_run` / `concurrency` | `6` / `2` | analyses per 15-minute run, and parallel analysis processes |
|
|
38
|
+
| `plan_limit_pct` | `0` | pause automatic analysis while your Claude plan's session (5hr) or weekly (7 day) limit is at or above this percent (Claude Code as the analyzer, with the status-line collector); `0` never pauses. Set it, and the limits above, from the usage chip in the dashboard's status bar |
|
|
38
39
|
| `backfill` | `true` | also analyze sessions recorded before install (newest first) |
|
|
39
40
|
| `chunk_chars` | `150000` | characters of condensed transcript per call; longer sessions are map-reduced |
|
|
40
41
|
| `timeout_seconds` | `900` | wall-clock limit per call |
|
|
@@ -35,6 +35,7 @@
|
|
|
35
35
|
| `idle_minutes` | `20` | 分析されるには、セッションが終了しているか、この時間アイドルである必要があります |
|
|
36
36
|
| `min_prompts` | `1` | 人間のプロンプトがこれより少ないセッションはスキップされます |
|
|
37
37
|
| `max_per_run` / `concurrency` | `6` / `2` | 15 分ごとの実行 1 回あたりの分析数と、並列に動かす分析プロセスの数 |
|
|
38
|
+
| `plan_limit_pct` | `0` | Claude プランのセッション(5 時間)または週間(7 日)の上限がこの割合以上のあいだ、自動分析を止めます(分析エンジンが Claude Code で、ステータスラインの収集があるとき)。`0` は止めません。これと上の上限は、ダッシュボードのステータスバーの使用量から設定できます |
|
|
38
39
|
| `backfill` | `true` | インストール前に記録されたセッションも分析する(新しい順) |
|
|
39
40
|
| `chunk_chars` | `150000` | 1 回の呼び出しに渡す、まとめたトランスクリプトの文字数。これより長いセッションは map-reduce で処理します |
|
|
40
41
|
| `timeout_seconds` | `900` | 呼び出し 1 回あたりの実時間の上限 |
|
|
@@ -22,6 +22,8 @@ Interlatch の Firebase プロジェクトから、公開されている求人
|
|
|
22
22
|
対戦は公開されたシートからあなたのコンピューターで計算されます。**アリーナを出る**(またはボードから抜ける)とシートは削除されます
|
|
23
23
|
([アリーナ](party.md#アリーナ))。
|
|
24
24
|
claude.ai や ChatGPT のエクスポートを取り込むときはチャットだけを読み、アカウントのファイル(`users.json`、`user.json`)は開きません。
|
|
25
|
+
ブラウザ拡張を読み込んだ場合、拡張は claude.ai と chatgpt.com のチャットだけをあなたのサインインで読み、このコンピューターの
|
|
26
|
+
ダッシュボードにだけ送ります([ソース](sources.md#ブラウザ拡張でチャットを保存する))。
|
|
25
27
|
提案した行が公開リポジトリに入る前に警告するため、GitHub のリモートがあるプロジェクトでは、あなた自身の `gh` のログインで
|
|
26
28
|
`gh repo view` を実行します。GitHub に送るのはリポジトリの名前だけで、結果は 1 週間覚えておきます。`gh` が
|
|
27
29
|
なければ何も送りません([提案](suggestions.md#プライバシー))。
|
|
@@ -75,13 +75,14 @@ IDE はそれ以外の会話ファイルをローカルに保存しません。
|
|
|
75
75
|
接続すると `~/.config/opencode/opencode.json` に MCP サーバーが登録されます(OpenCode はこれを `opencode.jsonc` と
|
|
76
76
|
合わせて読みます。`opencode.jsonc` は書き換えません)。
|
|
77
77
|
|
|
78
|
-
**claude.ai と ChatGPT のチャット**は Mac
|
|
78
|
+
**claude.ai と ChatGPT のチャット**は Mac に保存されません。ブラウザ拡張がサイトを使うたびに保存し
|
|
79
|
+
([下記](#ブラウザ拡張でチャットを保存する))、それより前のチャットはデータのエクスポートから取り込みます。claude.ai は
|
|
79
80
|
**設定 › プライバシー › データをエクスポート**、ChatGPT は **設定 › データコントロール › データをエクスポート** を開くと、届いたメールのリンクから
|
|
80
|
-
`.zip` をダウンロードできます。Sources ページの **Import export…**(カード *
|
|
81
|
+
`.zip` をダウンロードできます。Sources ページの **Import export…**(カード *Web chats*)、または `interlatch import <zip>` で取り込みます
|
|
81
82
|
(展開したフォルダーや `conversations.json` も使えます)。どちらのサービスのものかは中身から判別します。各チャットはセッション(エージェント
|
|
82
83
|
`claude-ai` または `chatgpt`、`source = claude-ai-export` または `chatgpt-export`)になり、プロンプト、応答、思考、ツール呼び出し
|
|
83
84
|
(claude.ai のアーティファクト、ChatGPT の Python・ブラウズ・画像生成とその出力)、添付ファイルや画像の注記が入ります。claude.ai のプロジェクト内の
|
|
84
|
-
チャットは `claude.ai/<プロジェクト>`、それ以外は `claude.ai`、ChatGPT のチャットは `chatgpt.com`
|
|
85
|
+
チャットは `claude.ai/<プロジェクト>`、それ以外は `claude.ai`、ChatGPT のチャットは `chatgpt.com`(ブラウザ拡張で保存したプロジェクト内のチャットは `chatgpt.com/<プロジェクト>`)にまとまります。ChatGPT のチャットは画面に表示される
|
|
85
86
|
分岐に沿って読むため、編集前のプロンプトや再生成で切り替えた回答は含まれません。新しいエクスポートはいつでも取り込めます。新しいチャットと
|
|
86
87
|
変更されたチャットだけが追加されます。読み込んでアーカイブするのはチャット(`conversations.json` と、プロジェクト名のための claude.ai の
|
|
87
88
|
`projects.json`)だけで、アカウントのファイル(`users.json`、`user.json`)と ChatGPT の `chat.html` は開きません。
|
|
@@ -90,11 +91,39 @@ IDE はそれ以外の会話ファイルをローカルに保存しません。
|
|
|
90
91
|
エクスポートにはトークン数がないため、費用は表示されません。Claude Code on the web のセッションは claude.ai のエクスポートに含まれません。
|
|
91
92
|
`claude --teleport <id>` で Mac に通常の Claude Code のトランスクリプトとして取り込めます。
|
|
92
93
|
|
|
94
|
+
### ブラウザ拡張でチャットを保存する
|
|
95
|
+
|
|
96
|
+
ブラウザ拡張は、claude.ai と ChatGPT を使うたびにチャットを保存します。新しいチャットのために改めてエクスポートする必要はありません。
|
|
97
|
+
Chrome、Edge、Brave などの Chromium 系ブラウザで動きます。Chrome ウェブストアにはないため、ダッシュボードが用意するフォルダーから読み込みます。
|
|
98
|
+
|
|
99
|
+
ダッシュボードの **設定 › ブラウザ拡張** に、Chrome、Edge、Brave それぞれのインストール手順と、拡張がこれまでに送ったものが表示されます。要点は次のとおりです。
|
|
100
|
+
|
|
101
|
+
1. `chrome://extensions`(Edge は `edge://extensions`)を開き、**デベロッパー モード**をオンにします。
|
|
102
|
+
2. **パッケージ化されていない拡張機能を読み込む**で `~/.interlatch/browser-extension` を選びます(パスはそのページからコピーできます)。
|
|
103
|
+
3. claude.ai か chatgpt.com を開きます。
|
|
104
|
+
|
|
105
|
+
どちらかのサイトのタブが開いている間、拡張は前回から変わったチャットをサイトに問い合わせます(タブを開いたとき、5 分ごと、そしてチャットを離れて数秒後。別のチャットやタブ、アプリに移ったときで、1 分に 1 回まで)。
|
|
106
|
+
変わったチャットをサイト自身のページと同じ方法で、あなたのサインインで読み、このコンピューターのダッシュボードに送ります。最初の同期は
|
|
107
|
+
直近 30 日分です(拡張のポップアップで変えられます)。それより前のチャットはエクスポートから取り込みます。1 回の同期で読むのは 40 件までで、
|
|
108
|
+
次の同期は 2 分後に続くため、数百件ある最初の同期には時間がかかります。サイトから速度を落とすよう求められたときは 10 分待ちます。
|
|
109
|
+
一時チャットは含めません。ChatGPT のプロジェクト内のチャットは `chatgpt.com/<プロジェクト>` にまとまります。
|
|
110
|
+
|
|
111
|
+
保存したチャットはエクスポートが更新するのと同じセッションで、同じように保存・アーカイブされます(最新のコピーは
|
|
112
|
+
`archive/<claude-ai|chatgpt>/captured/<id>/`)。取り込んだチャットと同じく**自動では分析しません**。選別して(下記)価値のあるものを
|
|
113
|
+
分析待ちに入れるか、チャットを開いて **Analyze now** を選びます。
|
|
114
|
+
|
|
115
|
+
拡張のツールバーボタンには、ダッシュボードにつながっているか、サイトごとの最後の同期とエラーが表示され、**Sync now**、**Paused**、
|
|
116
|
+
ダッシュボードのポート(`[server] port` を変えていなければ 11524)、最初の同期でさかのぼる日数を設定できます。読むのは claude.ai と
|
|
117
|
+
chatgpt.com だけで、送り先は `http://127.0.0.1` だけです。ダッシュボードは、Web ページが送れないヘッダーを付けた、このコンピューターからの
|
|
118
|
+
チャットだけを受け付けます。拡張は公開 API ではなくサイトの内部エンドポイントを使うため、サイトが変更すると、Interlatch の更新で対応するまで
|
|
119
|
+
ツールバーボタンにエラーが表示されます。Interlatch を更新するとダッシュボードがフォルダーを更新し、Chrome は再起動時(または
|
|
120
|
+
`chrome://extensions` の再読み込みボタン)に新しいバージョンを読み込みます。
|
|
121
|
+
|
|
93
122
|
### 取り込んだチャットの選別
|
|
94
123
|
|
|
95
124
|
チャット履歴の大半は、調べもの、文章の書き直し、日常の質問で、分析しても何も残りません。選別はチャットを
|
|
96
125
|
**分析する価値あり**(worth analyzing)、**たぶん**(maybe)、**価値なし**(not worth it)に分け、それぞれにトピックと
|
|
97
|
-
一行の理由を付けます。これで分析を役に立つチャットに回せます。エクスポートのカード(Sources ›
|
|
126
|
+
一行の理由を付けます。これで分析を役に立つチャットに回せます。エクスポートのカード(Sources › Web chats)で
|
|
98
127
|
**Screen N chats** を選ぶか、`interlatch screen` を実行します(`--sample 200` でまず無作為の 200 件で試す、
|
|
99
128
|
`--dry-run` は件数を数えるだけ)。
|
|
100
129
|
|
|
@@ -24,6 +24,8 @@ levels, and the team you field (which agents, in what order), with your board na
|
|
|
24
24
|
titles or prompts. Battles are worked out on your computer from the published sheets; **Leave the arena** (or leaving
|
|
25
25
|
the board) deletes your sheet ([The arena](party.md#the-arena)).
|
|
26
26
|
Importing a claude.ai or ChatGPT export reads only the chats, never the account files (`users.json`, `user.json`).
|
|
27
|
+
The browser extension, if you load it, reads chats only on claude.ai and chatgpt.com, with your own sign-in, and sends
|
|
28
|
+
them only to the dashboard on this computer ([Sources](sources.md#saving-chats-as-you-use-the-sites)).
|
|
27
29
|
To warn before a suggested line lands in a public repository, Interlatch runs `gh repo view` with your own `gh` login
|
|
28
30
|
for projects with a GitHub remote; that sends GitHub the repository's name and nothing else, and the answer is
|
|
29
31
|
remembered for a week. Without `gh` nothing is sent ([Suggestions](suggestions.md#privacy)).
|
|
@@ -80,15 +80,16 @@ into the database, which it does on its first start. OpenCode has no session-end
|
|
|
80
80
|
by the 15-minute sync. Connecting registers the MCP server in `~/.config/opencode/opencode.json` (OpenCode merges it
|
|
81
81
|
with `opencode.jsonc`, which is never rewritten).
|
|
82
82
|
|
|
83
|
-
**Chats on claude.ai and ChatGPT** are not stored on your Mac
|
|
83
|
+
**Chats on claude.ai and ChatGPT** are not stored on your Mac. The browser extension saves them as you use the sites
|
|
84
|
+
([below](#saving-chats-as-you-use-the-sites)); older chats come in from a data export: on claude.ai,
|
|
84
85
|
**Settings › Privacy › Export data**; on ChatGPT, **Settings › Data controls › Export data**. The email's link
|
|
85
|
-
downloads a `.zip`. Import it with **Import export…** on the Sources page (card *
|
|
86
|
+
downloads a `.zip`. Import it with **Import export…** on the Sources page (card *Web chats*), or
|
|
86
87
|
`interlatch import <zip>` (the unpacked folder or `conversations.json` work too); which service it came from is
|
|
87
88
|
recognized from its contents. Each chat becomes a session (agent `claude-ai` or `chatgpt`, `source =
|
|
88
89
|
claude-ai-export` or `chatgpt-export`) with its prompts, replies, thinking, tool calls (claude.ai artifacts,
|
|
89
90
|
ChatGPT's Python, browsing and image steps, with their output) and notes of attached files and images. claude.ai
|
|
90
91
|
chats in a project are grouped under `claude.ai/<project>`, the rest under `claude.ai`; ChatGPT chats under
|
|
91
|
-
`chatgpt.com
|
|
92
|
+
`chatgpt.com` (or `chatgpt.com/<project>`, when the browser extension saved it). A ChatGPT chat is read along the branch ChatGPT shows, so edited prompts and regenerated answers you
|
|
92
93
|
moved away from are left out. Import a newer export any time: new and changed chats are added, unchanged ones
|
|
93
94
|
skipped. Only the chats are read and archived (`conversations.json`, and claude.ai's `projects.json` for project
|
|
94
95
|
names); the account files (`users.json`, `user.json`) and ChatGPT's `chat.html` are never opened, and a zip uploaded
|
|
@@ -99,11 +100,44 @@ choose **Analyze now**, or import with `--analyze` to queue them all. The export
|
|
|
99
100
|
no cost. Claude Code on the web sessions are not in the claude.ai export; `claude --teleport <id>` brings one onto your
|
|
100
101
|
Mac as an ordinary Claude Code transcript. Codex Cloud tasks are a separate source (above).
|
|
101
102
|
|
|
103
|
+
### Saving chats as you use the sites
|
|
104
|
+
|
|
105
|
+
The browser extension saves claude.ai and ChatGPT chats as you use the sites, so new chats need no new export. It
|
|
106
|
+
works in Chrome, Edge, Brave and other Chromium browsers. It isn't in the Chrome Web Store: you load it from a folder
|
|
107
|
+
the dashboard keeps for it.
|
|
108
|
+
|
|
109
|
+
**Settings › Browser extension** in the dashboard walks you through it, for Chrome, Edge or Brave, and shows what the
|
|
110
|
+
extension has sent so far. In short:
|
|
111
|
+
|
|
112
|
+
1. Open `chrome://extensions` (Edge: `edge://extensions`) and turn on **Developer mode**.
|
|
113
|
+
2. Choose **Load unpacked** and pick `~/.interlatch/browser-extension` (the page has the path to copy).
|
|
114
|
+
3. Open claude.ai or chatgpt.com.
|
|
115
|
+
|
|
116
|
+
While a tab of either site is open, the extension asks the site which chats changed since it last looked: when the
|
|
117
|
+
tab loads, every 5 minutes, and a few seconds after you leave a chat (for another chat, tab or app; at most once a
|
|
118
|
+
minute). It reads each changed chat the way the site's own page does, with your sign-in, and
|
|
119
|
+
sends it to the dashboard on this computer. The first sync goes back 30 days (change it in the extension's popup);
|
|
120
|
+
older chats come in from an export. A sync reads at most 40 chats and the next follows two minutes later, so a first
|
|
121
|
+
sync of a few hundred chats takes a while; when a site asks it to slow down, it waits ten minutes. Temporary chats
|
|
122
|
+
are left out. Chats in a ChatGPT project are grouped under `chatgpt.com/<project>`.
|
|
123
|
+
|
|
124
|
+
A saved chat is the same session an export updates, stored and archived the same way (its latest copy in
|
|
125
|
+
`archive/<claude-ai|chatgpt>/captured/<id>/`). Like imported chats, saved chats are **not analyzed automatically**:
|
|
126
|
+
screen them (below) and queue the ones worth it, or choose **Analyze now** on one.
|
|
127
|
+
|
|
128
|
+
The extension's toolbar button shows whether it reaches the dashboard, each site's last sync and any error, with
|
|
129
|
+
**Sync now**, **Paused**, the dashboard's port (11524 unless you changed `[server] port`) and how far back the first
|
|
130
|
+
sync goes. It reads only claude.ai and chatgpt.com and sends only to `http://127.0.0.1`. The dashboard takes chats
|
|
131
|
+
only from this computer, with a header no web page can send. The extension uses the sites' internal endpoints, not a
|
|
132
|
+
published API: when a site changes them, the toolbar button shows the error until an Interlatch update catches up.
|
|
133
|
+
After an Interlatch update the dashboard refreshes the folder, and Chrome loads the new version when it restarts (or
|
|
134
|
+
with the reload button on `chrome://extensions`).
|
|
135
|
+
|
|
102
136
|
### Screening imported chats
|
|
103
137
|
|
|
104
138
|
Most of a chat history is lookups, rewrites and everyday questions that analysis would turn into nothing. Screening
|
|
105
139
|
sorts the chats into **worth analyzing**, **maybe** and **not worth it**, with a topic and a one-line reason each,
|
|
106
|
-
so the analysis goes where it pays. Choose **Screen N chats** on the export's card (Sources ›
|
|
140
|
+
so the analysis goes where it pays. Choose **Screen N chats** on the export's card (Sources › Web chats), or run
|
|
107
141
|
`interlatch screen` (`--sample 200` tries it on a random 200 first, `--dry-run` only counts).
|
|
108
142
|
|
|
109
143
|
- It reads only each chat's opening: title, date, the first and last prompt and the start of the first reply,
|
|
@@ -21,6 +21,7 @@ a = Analysis(
|
|
|
21
21
|
# the package metadata carries the version (interlatch.__version__ reads it); the Party reads its roadmap catalog,
|
|
22
22
|
# and the job-market numbers it shows until Firebase answers
|
|
23
23
|
datas=[(str(ROOT / "src" / "interlatch" / "web"), "interlatch/web"),
|
|
24
|
+
(str(ROOT / "src" / "interlatch" / "browser_extension"), "interlatch/browser_extension"),
|
|
24
25
|
(str(ROOT / "src" / "interlatch" / "knowledge" / "roadmap.json"), "interlatch/knowledge"),
|
|
25
26
|
(str(ROOT / "src" / "interlatch" / "knowledge" / "market.json"), "interlatch/knowledge")]
|
|
26
27
|
+ copy_metadata("interlatch"),
|
|
@@ -95,12 +95,36 @@ def queue_block(conn, cfg: Config, runner: Runner | None = None) -> str | None:
|
|
|
95
95
|
return tr("analysis is off (record only): pick an analyzer in Status › Analysis to analyze sessions")
|
|
96
96
|
if not cfg.analysis.auto:
|
|
97
97
|
return tr("automatic analysis is off (analysis.auto); analyze it from its page or with `interlatch analyze`")
|
|
98
|
+
over = plan_over_limit(conn, cfg)
|
|
99
|
+
if over:
|
|
100
|
+
return tr("your Claude plan's {window} limit is at {pct}%, at or above the {cap}% you set; it resumes after {when}",
|
|
101
|
+
window=tr(WINDOW_LABEL.get(over["window"], over["window"])), pct=round(over["used_pct"]),
|
|
102
|
+
cap=cfg.analysis.plan_limit_pct, when=local_str(over["resets_at"], "%m-%d %H:%M"))
|
|
98
103
|
runner = runner or make_runner(cfg)
|
|
99
104
|
if not runner.available():
|
|
100
105
|
return tr("{reason}, so nothing can be analyzed", reason=runner.unavailable_reason())
|
|
101
106
|
return None
|
|
102
107
|
|
|
103
108
|
|
|
109
|
+
WINDOW_LABEL = {"five_hour": "session (5hr)", "seven_day": "weekly (7 day)", "spend_limit": "spend"} # Claude Code's names
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def plan_over_limit(conn, cfg: Config) -> dict | None:
|
|
113
|
+
"""The Claude plan window (statusline.py) at or above analysis.plan_limit_pct, while Claude Code analyzes: its
|
|
114
|
+
name, used_pct and resets_at. None when the cap is off, another analyzer runs, or no window is that full."""
|
|
115
|
+
cap = cfg.analysis.plan_limit_pct
|
|
116
|
+
if not cap or cap <= 0 or cfg.analysis.backend != "claude":
|
|
117
|
+
return None
|
|
118
|
+
from ..runtime.statusline import plan_usage
|
|
119
|
+
|
|
120
|
+
windows = (plan_usage(conn) or {}).get("limits") or {}
|
|
121
|
+
over = [(name, w) for name, w in windows.items() if name in WINDOW_LABEL and (w.get("used_pct") or 0) >= cap]
|
|
122
|
+
if not over:
|
|
123
|
+
return None
|
|
124
|
+
name, w = max(over, key=lambda x: x[1]["resets_at"]) # the one that frees up last
|
|
125
|
+
return {"window": name, "used_pct": w["used_pct"], "resets_at": w["resets_at"]}
|
|
126
|
+
|
|
127
|
+
|
|
104
128
|
def count_pending(conn, cfg: Config) -> dict:
|
|
105
129
|
"""The analysis queue: `ready` now, `queued` (everything that will be analyzed by itself, ready included),
|
|
106
130
|
`held` (needs a change first), each with a count per reason, and `block` when the whole queue is stopped."""
|
|
@@ -9,7 +9,7 @@ import sqlite3
|
|
|
9
9
|
from collections.abc import Callable
|
|
10
10
|
from datetime import timedelta
|
|
11
11
|
|
|
12
|
-
from ..analysis.queue import PAUSE_KEY, pending_sessions
|
|
12
|
+
from ..analysis.queue import PAUSE_KEY, pending_sessions, plan_over_limit
|
|
13
13
|
from ..analysis.runners.base import LLMError, Runner, UsageLimitError
|
|
14
14
|
from ..analysis.runners.factory import make_runner
|
|
15
15
|
from ..analysis.worker import WorkReport, analyze_many
|
|
@@ -92,7 +92,12 @@ def _run_locked(cfg: Config, conn, report: WorkReport, *, session_ids, max_analy
|
|
|
92
92
|
conn.commit()
|
|
93
93
|
runner = make_runner(cfg)
|
|
94
94
|
paused = kv_get(conn, PAUSE_KEY)
|
|
95
|
-
|
|
95
|
+
# at the plan limit the user set (analysis.plan_limit_pct): runs on their own wait; sessions asked for still run
|
|
96
|
+
capped = None if session_ids or force else plan_over_limit(conn, cfg)
|
|
97
|
+
if capped:
|
|
98
|
+
report.note = (f"Claude plan's {capped['window']} limit at {capped['used_pct']:.0f}% "
|
|
99
|
+
f"(analysis.plan_limit_pct = {cfg.analysis.plan_limit_pct}); analysis skipped")
|
|
100
|
+
if analyze and not capped and (cfg.analysis.auto or session_ids or force):
|
|
96
101
|
if paused and paused > to_iso(utcnow()) and not session_ids:
|
|
97
102
|
report.paused_until = paused
|
|
98
103
|
elif not runner.available():
|
|
@@ -102,9 +107,9 @@ def _run_locked(cfg: Config, conn, report: WorkReport, *, session_ids, max_analy
|
|
|
102
107
|
analyze_many(cfg, ids, runner, report, model=model, progress=progress)
|
|
103
108
|
auto = cfg.synthesis.auto or force
|
|
104
109
|
# the projects and groups whose weekly re-analysis is due: someone turned each on, so they run without [synthesis] auto
|
|
105
|
-
due = (_weekly_due(cfg, conn) if synthesize and not session_ids and runner.available()
|
|
106
|
-
and not (paused and paused > to_iso(utcnow())) else [])
|
|
107
|
-
if synthesize and (auto or due) and runner.available() and not report.paused_until:
|
|
110
|
+
due = (_weekly_due(cfg, conn) if synthesize and not session_ids and not capped and runner.available()
|
|
111
|
+
and not report.paused_until and not (paused and paused > to_iso(utcnow())) else [])
|
|
112
|
+
if synthesize and not capped and (auto or due) and runner.available() and not report.paused_until:
|
|
108
113
|
_synthesize(cfg, conn, runner, report, force=force, auto=auto, also=_weekly_projects(cfg, conn, due),
|
|
109
114
|
progress=progress)
|
|
110
115
|
_glossaries(cfg, conn, runner, report, progress=progress)
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/* Interlatch: save web chats. The service worker: the one part that talks to the dashboard on this computer.
|
|
2
|
+
|
|
3
|
+
capture.js runs in claude.ai and chatgpt.com tabs and reads the chats there; a page on https can't call
|
|
4
|
+
http://127.0.0.1, so it hands each chat here and this sends it to POST /api/capture (the dashboard's
|
|
5
|
+
chat_import.capture). The dashboard takes it only from this computer, with the X-Interlatch header no web page can
|
|
6
|
+
send it. */
|
|
7
|
+
"use strict";
|
|
8
|
+
|
|
9
|
+
const DEFAULT_PORT = 11524;
|
|
10
|
+
|
|
11
|
+
async function dashboard() {
|
|
12
|
+
const { port } = await chrome.storage.local.get("port");
|
|
13
|
+
return `http://127.0.0.1:${Number(port) || DEFAULT_PORT}`;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async function send(format, conversation) {
|
|
17
|
+
const base = await dashboard();
|
|
18
|
+
let res;
|
|
19
|
+
try {
|
|
20
|
+
res = await fetch(`${base}/api/capture`, {
|
|
21
|
+
method: "POST",
|
|
22
|
+
headers: { "Content-Type": "application/json", "X-Interlatch": "1" },
|
|
23
|
+
body: JSON.stringify({ format, conversation }),
|
|
24
|
+
});
|
|
25
|
+
} catch {
|
|
26
|
+
return { ok: false, retry: true, error: `the Interlatch dashboard is not running at ${base}` };
|
|
27
|
+
}
|
|
28
|
+
const body = await res.json().catch(() => ({}));
|
|
29
|
+
if (res.ok) return { ok: true, ...body };
|
|
30
|
+
// only 400 is about the chat itself (not one the dashboard can read), so it isn't sent again until it changes; the
|
|
31
|
+
// rest is the dashboard's: 409 a sync holds the archive, 404 a version without /api/capture, 403 not this computer's
|
|
32
|
+
if (res.status === 404) return { ok: false, retry: true, error: "update Interlatch: this dashboard can't take chats yet" };
|
|
33
|
+
return { ok: false, retry: res.status !== 400, error: body.error || `the dashboard answered ${res.status}` };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async function status() {
|
|
37
|
+
const base = await dashboard();
|
|
38
|
+
try {
|
|
39
|
+
const res = await fetch(`${base}/api/me`, { headers: { "X-Interlatch": "1" } });
|
|
40
|
+
const me = await res.json();
|
|
41
|
+
if (!res.ok) return { base, ok: false, error: me.error || `answered ${res.status}` };
|
|
42
|
+
return { base, ok: me.viewer?.here !== false, error: me.viewer?.here === false ? "not this computer's dashboard" : null };
|
|
43
|
+
} catch {
|
|
44
|
+
return { base, ok: false, error: "not running" };
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
chrome.runtime.onMessage.addListener((msg, sender, reply) => {
|
|
49
|
+
if (msg?.type === "capture" && sender.tab) {
|
|
50
|
+
send(msg.format, msg.conversation).then(reply);
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
if (msg?.type === "status" && !sender.tab) {
|
|
54
|
+
status().then(reply);
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
return false;
|
|
58
|
+
});
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
/* Interlatch: save web chats. Runs in claude.ai and chatgpt.com tabs.
|
|
2
|
+
|
|
3
|
+
While a tab of the site is open (on load, then every few minutes), it asks the site's own conversation list which
|
|
4
|
+
chats changed since the last sync, reads each one the way the site's page does (same origin, your own sign-in), and
|
|
5
|
+
hands it to background.js for the dashboard. A chat is read in the JSON a data export holds, so the dashboard stores
|
|
6
|
+
it as an import would, as the same session. The first sync goes back `days` (30 by default); older chats come in from a
|
|
7
|
+
data export. Temporary chats are left out. A sync reads at most MAX chats and the next one follows a few minutes
|
|
8
|
+
later, so a first sync with many chats doesn't run into the site's rate limit; when the site asks to slow down anyway
|
|
9
|
+
(429), the sync waits longer.
|
|
10
|
+
|
|
11
|
+
These are the sites' internal endpoints, not a published API: if a site changes them, the popup shows the error. */
|
|
12
|
+
"use strict";
|
|
13
|
+
|
|
14
|
+
(() => {
|
|
15
|
+
const SITE = { "claude.ai": "claude-ai", "chatgpt.com": "chatgpt" }[location.hostname];
|
|
16
|
+
if (!SITE) return;
|
|
17
|
+
const KEY = `sync:${SITE}`;
|
|
18
|
+
const LOCK = `lock:${SITE}`; // one tab syncs a site at a time
|
|
19
|
+
const EVERY = 5 * 60e3;
|
|
20
|
+
const PAUSE = 700; // between two chats read: the pace of someone clicking through them
|
|
21
|
+
const MAX = 40; // chats read in one sync: ChatGPT answered 429 after about 200 in a row
|
|
22
|
+
const AGAIN = 2 * 60e3; // the next sync, when this one stopped at MAX
|
|
23
|
+
const SLOWED = 10 * 60e3; // the next sync, after a 429
|
|
24
|
+
const SOON_DELAY = 5e3; // after you leave a chat: the site has saved its last reply by then
|
|
25
|
+
const SOON_GAP = 60e3;
|
|
26
|
+
const MARGIN = 10 * 60e3; // the next sync looks this far before the last one began
|
|
27
|
+
const STALE_LOCK = 15 * 60e3;
|
|
28
|
+
const DEFAULT_DAYS = 30;
|
|
29
|
+
const DAY = 86400e3;
|
|
30
|
+
|
|
31
|
+
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
32
|
+
const time = (v) => { // an ISO date, or seconds (ChatGPT) or milliseconds since 1970
|
|
33
|
+
const n = typeof v === "number" ? v : v !== "" && v != null && !isNaN(Number(v)) ? Number(v) : NaN;
|
|
34
|
+
return isNaN(n) ? Date.parse(v) || 0 : n < 1e12 ? n * 1000 : n;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
async function getJSON(path, headers) {
|
|
38
|
+
const res = await fetch(path, { credentials: "include", headers });
|
|
39
|
+
if (!res.ok) {
|
|
40
|
+
throw Object.assign(new Error(`${location.hostname}${path.split("?")[0]} answered ${res.status}`), { status: res.status });
|
|
41
|
+
}
|
|
42
|
+
return res.json();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Each site: the chats changed since `since` (ms), newest first, and one chat as its data export holds it.
|
|
46
|
+
const claude = {
|
|
47
|
+
async *changed(since) {
|
|
48
|
+
const orgs = await getJSON("/api/organizations");
|
|
49
|
+
for (const org of Array.isArray(orgs) ? orgs : []) {
|
|
50
|
+
if (Array.isArray(org.capabilities) && !org.capabilities.includes("chat")) continue; // an API-only organization
|
|
51
|
+
for (let offset = 0; ; offset += 50) {
|
|
52
|
+
const page = await getJSON(`/api/organizations/${org.uuid}/chat_conversations?limit=50&offset=${offset}`);
|
|
53
|
+
if (!Array.isArray(page) || !page.length) break;
|
|
54
|
+
let older = false;
|
|
55
|
+
for (const c of page) {
|
|
56
|
+
if (time(c.updated_at) < since) { older = true; continue; }
|
|
57
|
+
if (!c.is_temporary) yield { id: c.uuid, updated: c.updated_at, org: org.uuid, item: c };
|
|
58
|
+
}
|
|
59
|
+
if (older || page.length < 50) break;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
async read(it) {
|
|
64
|
+
const conv = await getJSON(
|
|
65
|
+
`/api/organizations/${it.org}/chat_conversations/${it.id}?tree=True&rendering_mode=messages&render_all_tools=true`);
|
|
66
|
+
// the chat itself doesn't name its project; the list does
|
|
67
|
+
if (it.item.project && !conv.project) conv.project = it.item.project;
|
|
68
|
+
if (it.item.project_uuid && !conv.project_uuid) conv.project_uuid = it.item.project_uuid;
|
|
69
|
+
return conv;
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const chatgpt = {
|
|
74
|
+
token: null,
|
|
75
|
+
async headers() {
|
|
76
|
+
if (!this.token) {
|
|
77
|
+
const session = await getJSON("/api/auth/session");
|
|
78
|
+
if (!session.accessToken) throw new Error("not signed in to ChatGPT");
|
|
79
|
+
this.token = session.accessToken;
|
|
80
|
+
}
|
|
81
|
+
const device = document.cookie.match(/(?:^|; )oai-did=([^;]+)/)?.[1];
|
|
82
|
+
return { Authorization: `Bearer ${this.token}`, ...(device ? { "oai-device-id": decodeURIComponent(device) } : {}) };
|
|
83
|
+
},
|
|
84
|
+
async *listed(path, since, project) {
|
|
85
|
+
const h = await this.headers();
|
|
86
|
+
for (let offset = 0; ; offset += 28) {
|
|
87
|
+
const page = await getJSON(`${path}offset=${offset}&limit=28&order=updated`, h);
|
|
88
|
+
const items = page.items || [];
|
|
89
|
+
let older = false;
|
|
90
|
+
for (const c of items) {
|
|
91
|
+
if (time(c.update_time) < since) { older = true; continue; }
|
|
92
|
+
if (!c.is_temporary_chat) yield { id: c.id, updated: String(c.update_time), project };
|
|
93
|
+
}
|
|
94
|
+
if (older || items.length < 28) break;
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
async *changed(since) {
|
|
98
|
+
this.token = null; // a fresh one each sync: it expires
|
|
99
|
+
yield* this.listed("/backend-api/conversations?", since, null);
|
|
100
|
+
// chats in a ChatGPT project are not in that list: each project lists its own, newest first
|
|
101
|
+
const h = await this.headers();
|
|
102
|
+
let cursor = null;
|
|
103
|
+
do {
|
|
104
|
+
const side = await getJSON(`/backend-api/gizmos/snorlax/sidebar?conversations_per_gizmo=0${
|
|
105
|
+
cursor ? `&cursor=${encodeURIComponent(cursor)}` : ""}`, h);
|
|
106
|
+
const items = side.items || [];
|
|
107
|
+
for (const g of items) {
|
|
108
|
+
const giz = g.gizmo?.gizmo || g.gizmo || {};
|
|
109
|
+
if (!giz.id) continue;
|
|
110
|
+
const project = { id: giz.id, name: giz.display?.name || "" };
|
|
111
|
+
for (let c = "0"; c != null;) {
|
|
112
|
+
const page = await getJSON(`/backend-api/gizmos/${giz.id}/conversations?cursor=${encodeURIComponent(c)}`, h);
|
|
113
|
+
let older = false;
|
|
114
|
+
for (const conv of page.items || []) {
|
|
115
|
+
if (time(conv.update_time) < since) { older = true; continue; }
|
|
116
|
+
yield { id: conv.id, updated: String(conv.update_time), project };
|
|
117
|
+
}
|
|
118
|
+
c = older || !(page.items || []).length ? null : page.cursor ?? null;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
cursor = items.length ? side.cursor : null;
|
|
122
|
+
} while (cursor);
|
|
123
|
+
},
|
|
124
|
+
async read(it) {
|
|
125
|
+
const conv = await getJSON(`/backend-api/conversation/${it.id}`, await this.headers());
|
|
126
|
+
if (it.project) conv.project = it.project;
|
|
127
|
+
return conv;
|
|
128
|
+
},
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
const site = SITE === "claude-ai" ? claude : chatgpt;
|
|
132
|
+
let running = false;
|
|
133
|
+
|
|
134
|
+
async function sync() {
|
|
135
|
+
if (running) return;
|
|
136
|
+
running = true;
|
|
137
|
+
try {
|
|
138
|
+
const { [KEY]: saved, [LOCK]: lock, days, paused } = await chrome.storage.local.get([KEY, LOCK, "days", "paused"]);
|
|
139
|
+
const started = Date.now();
|
|
140
|
+
if (paused || (lock && started - lock < STALE_LOCK)) return;
|
|
141
|
+
await chrome.storage.local.set({ [LOCK]: started });
|
|
142
|
+
const state = { seen: {}, captured: 0, ...saved, running: started, progress: 0 }; // the popup shows the progress
|
|
143
|
+
await chrome.storage.local.set({ [KEY]: state });
|
|
144
|
+
const since = state.since ? state.since - MARGIN : started - (Number(days) || DEFAULT_DAYS) * DAY;
|
|
145
|
+
let sent = 0, read = 0, error = null, more = false, slowed = false;
|
|
146
|
+
const done = new Set();
|
|
147
|
+
try {
|
|
148
|
+
for await (const it of site.changed(since)) {
|
|
149
|
+
if (done.has(it.id)) continue;
|
|
150
|
+
done.add(it.id);
|
|
151
|
+
if (state.seen[it.id] === it.updated) continue; // sent already, and not changed since
|
|
152
|
+
if (read++ >= MAX) { more = true; break; }
|
|
153
|
+
const conv = await site.read(it);
|
|
154
|
+
let r;
|
|
155
|
+
try {
|
|
156
|
+
r = await chrome.runtime.sendMessage({ type: "capture", format: SITE, conversation: conv });
|
|
157
|
+
} catch {
|
|
158
|
+
r = { retry: true, error: "the extension was reloaded: reload this tab" };
|
|
159
|
+
}
|
|
160
|
+
if (!r?.ok && r?.retry !== false) { error = r?.error || "could not reach the dashboard"; break; }
|
|
161
|
+
state.seen[it.id] = it.updated; // sent, or refused for good (not sent again until it changes)
|
|
162
|
+
if (r.ok && (r.result === "new" || r.result === "updated")) state.progress = ++sent;
|
|
163
|
+
if (!r.ok) state.refused = { at: Date.now(), error: r.error };
|
|
164
|
+
await chrome.storage.local.set({ [KEY]: state });
|
|
165
|
+
await sleep(PAUSE);
|
|
166
|
+
}
|
|
167
|
+
} catch (e) {
|
|
168
|
+
if (e?.status === 429) more = slowed = true;
|
|
169
|
+
else error = String(e?.message || e);
|
|
170
|
+
}
|
|
171
|
+
if (!error && !more) { // a later sync looks only from here; chats seen before that can't come up again unchanged
|
|
172
|
+
state.since = started;
|
|
173
|
+
for (const [id, updated] of Object.entries(state.seen)) if (time(updated) < started - MARGIN) delete state.seen[id];
|
|
174
|
+
}
|
|
175
|
+
Object.assign(state, { lastRun: Date.now(), error, more, slowed, running: null, captured: (state.captured || 0) + sent });
|
|
176
|
+
if (sent) state.lastSent = Date.now();
|
|
177
|
+
await chrome.storage.local.set({ [KEY]: state });
|
|
178
|
+
await chrome.storage.local.remove(LOCK);
|
|
179
|
+
if (more) setTimeout(sync, slowed ? SLOWED : AGAIN); // the rest, a little later
|
|
180
|
+
} finally {
|
|
181
|
+
running = false;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const here = () => chrome.storage.local.set({ [`alive:${SITE}`]: Date.now() }); // the popup knows a tab answers
|
|
186
|
+
here();
|
|
187
|
+
setTimeout(sync, 3000);
|
|
188
|
+
setInterval(sync, EVERY);
|
|
189
|
+
// the chat you were in, without waiting for the next 5 minutes: when you leave it for another chat (the sites
|
|
190
|
+
// change the address without loading a page) or for another tab or app; at most once a minute
|
|
191
|
+
let lastSoon = 0, path = location.pathname;
|
|
192
|
+
const soon = () => {
|
|
193
|
+
if (Date.now() - lastSoon < SOON_GAP) return;
|
|
194
|
+
lastSoon = Date.now();
|
|
195
|
+
setTimeout(sync, SOON_DELAY);
|
|
196
|
+
};
|
|
197
|
+
setInterval(() => { if (location.pathname !== path) { path = location.pathname; soon(); } }, 2000);
|
|
198
|
+
document.addEventListener("visibilitychange", () => { if (document.visibilityState === "hidden") soon(); });
|
|
199
|
+
chrome.storage.onChanged.addListener((changes) => { // Sync now, from the popup
|
|
200
|
+
if (changes.syncNow?.newValue) { here(); sync(); }
|
|
201
|
+
});
|
|
202
|
+
})();
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"manifest_version": 3,
|
|
3
|
+
"name": "Interlatch: save web chats",
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"description": "Sends your claude.ai and ChatGPT chats to the Interlatch dashboard on this computer as you use the sites.",
|
|
6
|
+
"icons": { "16": "icon-16.png", "32": "icon-32.png", "48": "icon-48.png", "128": "icon-128.png" },
|
|
7
|
+
"action": {
|
|
8
|
+
"default_title": "Interlatch",
|
|
9
|
+
"default_popup": "popup.html",
|
|
10
|
+
"default_icon": { "16": "icon-16.png", "32": "icon-32.png" }
|
|
11
|
+
},
|
|
12
|
+
"background": { "service_worker": "background.js" },
|
|
13
|
+
"permissions": ["storage"],
|
|
14
|
+
"host_permissions": ["http://127.0.0.1/*"],
|
|
15
|
+
"content_scripts": [
|
|
16
|
+
{ "matches": ["https://claude.ai/*", "https://chatgpt.com/*"], "js": ["capture.js"], "run_at": "document_idle" }
|
|
17
|
+
]
|
|
18
|
+
}
|