canfar-lab 0.7.0__py3-none-any.whl
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.
- canfar_lab/__init__.py +20 -0
- canfar_lab/__main__.py +4 -0
- canfar_lab/agent/__init__.py +5 -0
- canfar_lab/agent/addons.py +230 -0
- canfar_lab/agent/agent_config.py +593 -0
- canfar_lab/agent/agent_targets.py +285 -0
- canfar_lab/agent/bin_root.py +383 -0
- canfar_lab/agent/bundle_path.py +48 -0
- canfar_lab/agent/clean_agent.py +97 -0
- canfar_lab/agent/fix.py +236 -0
- canfar_lab/agent/install.py +440 -0
- canfar_lab/agent/install_bins.py +407 -0
- canfar_lab/agent/install_fetch.py +577 -0
- canfar_lab/agent/interact.py +130 -0
- canfar_lab/agent/inventory.py +194 -0
- canfar_lab/agent/keys.py +338 -0
- canfar_lab/agent/opencode_config.py +89 -0
- canfar_lab/agent/openscience.py +275 -0
- canfar_lab/agent/plugins.py +620 -0
- canfar_lab/agent/reconcile.py +236 -0
- canfar_lab/agent/registry.py +72 -0
- canfar_lab/agent/registry_lifecycle.py +718 -0
- canfar_lab/agent/registry_probe.py +376 -0
- canfar_lab/agent/registry_store.py +78 -0
- canfar_lab/agent/review_bench.py +535 -0
- canfar_lab/agent/setup.py +921 -0
- canfar_lab/agent/setup_state.py +264 -0
- canfar_lab/agent/support.py +251 -0
- canfar_lab/agent/upstream.py +87 -0
- canfar_lab/agent/wipe.py +119 -0
- canfar_lab/cli/__init__.py +0 -0
- canfar_lab/cli/agent_cmd.py +378 -0
- canfar_lab/cli/agent_format.py +251 -0
- canfar_lab/cli/agent_install.py +603 -0
- canfar_lab/cli/agent_keys.py +105 -0
- canfar_lab/cli/agent_plugins.py +210 -0
- canfar_lab/cli/agent_setup.py +408 -0
- canfar_lab/cli/banner.py +76 -0
- canfar_lab/cli/clean.py +140 -0
- canfar_lab/cli/config.py +63 -0
- canfar_lab/cli/context.py +33 -0
- canfar_lab/cli/desk.py +218 -0
- canfar_lab/cli/dispatch.py +27 -0
- canfar_lab/cli/env.py +69 -0
- canfar_lab/cli/help_cmd.py +242 -0
- canfar_lab/cli/init_clone_env.py +506 -0
- canfar_lab/cli/kernel.py +109 -0
- canfar_lab/cli/main.py +113 -0
- canfar_lab/cli/open_cmd.py +134 -0
- canfar_lab/cli/panel.py +199 -0
- canfar_lab/cli/status.py +176 -0
- canfar_lab/cli/studio.py +522 -0
- canfar_lab/cli/sync.py +509 -0
- canfar_lab/config/__init__.py +3 -0
- canfar_lab/config/settings.py +118 -0
- canfar_lab/core/arc_permissions.py +109 -0
- canfar_lab/core/cadc_auth.py +21 -0
- canfar_lab/core/disk_usage.py +158 -0
- canfar_lab/core/git.py +156 -0
- canfar_lab/core/home_clean.py +144 -0
- canfar_lab/core/home_layout.py +410 -0
- canfar_lab/core/kernel.py +171 -0
- canfar_lab/core/pathlock.py +177 -0
- canfar_lab/core/paths.py +70 -0
- canfar_lab/core/project.py +361 -0
- canfar_lab/core/session_common.py +122 -0
- canfar_lab/core/session_resources.py +166 -0
- canfar_lab/core/storage.py +303 -0
- canfar_lab/core/vospace_status.py +270 -0
- canfar_lab/data/__init__.py +0 -0
- canfar_lab/data/agent/VERSION +1 -0
- canfar_lab/data/agent/agents/agy.yaml +27 -0
- canfar_lab/data/agent/agents/augment.yaml +28 -0
- canfar_lab/data/agent/agents/claude.yaml +26 -0
- canfar_lab/data/agent/agents/cline.yaml +19 -0
- canfar_lab/data/agent/agents/codewhale.yaml +29 -0
- canfar_lab/data/agent/agents/codex.yaml +28 -0
- canfar_lab/data/agent/agents/copilot.yaml +26 -0
- canfar_lab/data/agent/agents/cursor.yaml +23 -0
- canfar_lab/data/agent/agents/droid.yaml +30 -0
- canfar_lab/data/agent/agents/dsh.yaml +23 -0
- canfar_lab/data/agent/agents/freebuff.yaml +28 -0
- canfar_lab/data/agent/agents/goose.yaml +24 -0
- canfar_lab/data/agent/agents/hermes.yaml +27 -0
- canfar_lab/data/agent/agents/junie.yaml +29 -0
- canfar_lab/data/agent/agents/kilo.yaml +25 -0
- canfar_lab/data/agent/agents/muse.yaml +27 -0
- canfar_lab/data/agent/agents/omp.yaml +29 -0
- canfar_lab/data/agent/agents/openclaw.yaml +22 -0
- canfar_lab/data/agent/agents/opencode.yaml +25 -0
- canfar_lab/data/agent/agents/openscience.yaml +33 -0
- canfar_lab/data/agent/agents/pi.yaml +31 -0
- canfar_lab/data/agent/agents/qoder.yaml +25 -0
- canfar_lab/data/agent/agents/swival.yaml +23 -0
- canfar_lab/data/agent/agents/zcode.yaml +25 -0
- canfar_lab/data/agent/claude/mcp.json +33 -0
- canfar_lab/data/agent/claude/settings.json +17 -0
- canfar_lab/data/agent/cli/agent-tools.sh +17 -0
- canfar_lab/data/agent/cline/cline-notes.md +7 -0
- canfar_lab/data/agent/codewhale/config.toml +12 -0
- canfar_lab/data/agent/codex/config.toml +30 -0
- canfar_lab/data/agent/copilot/mcp-config.json +42 -0
- canfar_lab/data/agent/cursor/mcp.json +33 -0
- canfar_lab/data/agent/cursor/rules/astroai-lab-paths.mdc +18 -0
- canfar_lab/data/agent/cursor/rules/cpp.mdc +13 -0
- canfar_lab/data/agent/cursor/rules/env-hygiene.mdc +44 -0
- canfar_lab/data/agent/cursor/rules/harness.mdc +17 -0
- canfar_lab/data/agent/cursor/rules/latex.mdc +15 -0
- canfar_lab/data/agent/cursor/rules/ponytail.mdc +36 -0
- canfar_lab/data/agent/cursor/rules/python-pixi.mdc +13 -0
- canfar_lab/data/agent/cursor/rules/python.mdc +15 -0
- canfar_lab/data/agent/cursor/rules/shell.mdc +14 -0
- canfar_lab/data/agent/cursor/rules/token-efficient.mdc +15 -0
- canfar_lab/data/agent/cursor/rules/writing.mdc +15 -0
- canfar_lab/data/agent/freebuff/astroai-notes.md +6 -0
- canfar_lab/data/agent/goose/extensions.yaml +43 -0
- canfar_lab/data/agent/goose/goosehints +6 -0
- canfar_lab/data/agent/kilo/kilo.jsonc +12 -0
- canfar_lab/data/agent/manifest.json +86 -0
- canfar_lab/data/agent/marimo/marimo.toml +17 -0
- canfar_lab/data/agent/muse/settings.json +3 -0
- canfar_lab/data/agent/omp/astroai-notes.md +23 -0
- canfar_lab/data/agent/opencode/opencode.json +42 -0
- canfar_lab/data/agent/openscience/agent/astronomy.md +25 -0
- canfar_lab/data/agent/openscience/canfar.md +81 -0
- canfar_lab/data/agent/openscience/command/astro-check.md +15 -0
- canfar_lab/data/agent/openscience/command/find-data.md +10 -0
- canfar_lab/data/agent/openscience/command/save-results.md +10 -0
- canfar_lab/data/agent/openscience/command/scale-out.md +12 -0
- canfar_lab/data/agent/openscience/skills/astro-literature/SKILL.md +55 -0
- canfar_lab/data/agent/openscience/skills/cadc-archive-search/SKILL.md +52 -0
- canfar_lab/data/agent/openscience/skills/canfar-data-management/SKILL.md +63 -0
- canfar_lab/data/agent/openscience/skills/canfar-ray-scaling/SKILL.md +76 -0
- canfar_lab/data/agent/openscience/skills/catalog-crossmatch/SKILL.md +79 -0
- canfar_lab/data/agent/openscience/skills/ccd-image-reduction/SKILL.md +79 -0
- canfar_lab/data/agent/openscience/skills/fits-wcs-cutouts/SKILL.md +74 -0
- canfar_lab/data/agent/openscience/skills/gaia-astrometry/SKILL.md +95 -0
- canfar_lab/data/agent/openscience/skills/lightcurve-periodogram/SKILL.md +53 -0
- canfar_lab/data/agent/openscience/skills/obscore-tap/SKILL.md +55 -0
- canfar_lab/data/agent/openscience/skills/observation-planning/SKILL.md +69 -0
- canfar_lab/data/agent/openscience/skills/photometric-calibration/SKILL.md +75 -0
- canfar_lab/data/agent/openscience/skills/photometry-aperture/SKILL.md +65 -0
- canfar_lab/data/agent/openscience/skills/spectra-specutils/SKILL.md +58 -0
- canfar_lab/data/agent/openscience/skills/spectral-cubes/SKILL.md +68 -0
- canfar_lab/data/agent/openscience/skills/vo-cutouts-datalink/SKILL.md +76 -0
- canfar_lab/data/agent/pi/settings.json +4 -0
- canfar_lab/data/agent/plugins/ast-grep-cli.yaml +15 -0
- canfar_lab/data/agent/plugins/git-mcp.yaml +22 -0
- canfar_lab/data/agent/plugins/mcp-context7.yaml +12 -0
- canfar_lab/data/agent/plugins/mcp-memory.yaml +12 -0
- canfar_lab/data/agent/plugins/ponytail-rule.yaml +14 -0
- canfar_lab/data/agent/plugins/ray-manager-mcp.yaml +20 -0
- canfar_lab/data/agent/plugins/skore-cli.yaml +16 -0
- canfar_lab/data/agent/plugins/sqlite.yaml +26 -0
- canfar_lab/data/agent/plugins/time.yaml +23 -0
- canfar_lab/data/agent/plugins/token-efficient.yaml +13 -0
- canfar_lab/data/agent/project/.cursor/mcp.json +14 -0
- canfar_lab/data/agent/project/.cursor/rules/project-defaults.mdc +11 -0
- canfar_lab/data/agent/project/AGENTS.md +62 -0
- canfar_lab/data/agent/qoder/astroai-notes.md +6 -0
- canfar_lab/data/agent/rules/env-hygiene.md +39 -0
- canfar_lab/data/agent/rules/harness.md +10 -0
- canfar_lab/data/agent/rules/latex.md +7 -0
- canfar_lab/data/agent/rules/ponytail.md +24 -0
- canfar_lab/data/agent/rules/python-pixi.md +6 -0
- canfar_lab/data/agent/rules/shell.md +6 -0
- canfar_lab/data/agent/rules/token-efficient.md +3 -0
- canfar_lab/data/agent/rules/writing.md +7 -0
- canfar_lab/data/agent/support.yaml +87 -0
- canfar_lab/data/agent/swival/astroai-notes.md +6 -0
- canfar_lab/data/agent/zcode/astroai-notes.md +6 -0
- canfar_lab/data/brand/README.md +11 -0
- canfar_lab/data/brand/astroai-logo.png +0 -0
- canfar_lab/data/desk/agents.html +159 -0
- canfar_lab/data/desk/dock.html +230 -0
- canfar_lab/data/notebooks/ray_train.ipynb +43 -0
- canfar_lab/data/notebooks/starter.ipynb +460 -0
- canfar_lab/data/notebooks/starter.py +423 -0
- canfar_lab/data/review-bench/HOWTO.md +294 -0
- canfar_lab/data/review-bench/README.md +225 -0
- canfar_lab/data/review-bench/VERSION +1 -0
- canfar_lab/data/review-bench/bin/dsh-web.sh +40 -0
- canfar_lab/data/review-bench/bin/panel.sh +108 -0
- canfar_lab/data/review-bench/cordis.bench.patch.yml +12 -0
- canfar_lab/data/review-bench/presets/review-bench/agent.cordis.yml +682 -0
- canfar_lab/data/review-bench/presets/review-bench/preset.yml +8 -0
- canfar_lab/data/review-bench/project-template/.dsh/README.md +95 -0
- canfar_lab/data/review-bench/project-template/.dsh/cordis.patch.yml +21 -0
- canfar_lab/data/review-bench/skills/astroai-team-charter/SKILL.md +167 -0
- canfar_lab/data/review-bench/skills/canfar-session/SKILL.md +44 -0
- canfar_lab/data/review-bench/skills/review-panel/SKILL.md +258 -0
- canfar_lab/data/review-bench/skills/review-panel/references/brief-template.md +42 -0
- canfar_lab/data/review-bench/skills/review-panel/references/panel-round1.js +130 -0
- canfar_lab/data/review-bench/skills/review-panel/references/report-template.md +49 -0
- canfar_lab/data/review-bench/skills/review-panel/references/rubric.md +128 -0
- canfar_lab/data/review-bench/validate.mjs +314 -0
- canfar_lab/data/shell/hooks.sh +68 -0
- canfar_lab/data/shell/profile.sh +105 -0
- canfar_lab/data/studio/plugins/dsh-astroai-brand/cordis.patch.yml +8 -0
- canfar_lab/data/studio/plugins/dsh-astroai-brand/lib/client.js +81 -0
- canfar_lab/data/studio/plugins/dsh-astroai-brand/lib/index.js +2 -0
- canfar_lab/data/studio/plugins/dsh-astroai-brand/package.json +30 -0
- canfar_lab/data/studio/plugins/dsh-astroai-settings/cordis.patch.yml +4 -0
- canfar_lab/data/studio/plugins/dsh-astroai-settings/lib/client.js +308 -0
- canfar_lab/data/studio/plugins/dsh-astroai-settings/lib/index.js +111 -0
- canfar_lab/data/studio/plugins/dsh-astroai-settings/package.json +29 -0
- canfar_lab/data/studio/plugins/dsh-opencode-session/README.md +135 -0
- canfar_lab/data/studio/plugins/dsh-opencode-session/cordis.patch.yml +29 -0
- canfar_lab/data/studio/plugins/dsh-opencode-session/lib/index.js +216 -0
- canfar_lab/data/studio/plugins/dsh-opencode-session/package.json +53 -0
- canfar_lab/desk/__init__.py +15 -0
- canfar_lab/desk/actions.py +281 -0
- canfar_lab/desk/agents_page.py +84 -0
- canfar_lab/desk/apps.py +384 -0
- canfar_lab/desk/proxy.py +440 -0
- canfar_lab/desk/serve.py +79 -0
- canfar_lab/desk/state.py +155 -0
- canfar_lab/dsh_release.py +158 -0
- canfar_lab/errors.py +15 -0
- canfar_lab/models/__init__.py +3 -0
- canfar_lab/models/manifest.py +19 -0
- canfar_lab/panel.py +274 -0
- canfar_lab/session_urls.py +66 -0
- canfar_lab/shell/__init__.py +1 -0
- canfar_lab/shell/session_env.py +416 -0
- canfar_lab/studio.py +557 -0
- canfar_lab/studio_bundles.py +218 -0
- canfar_lab/studio_doctor.py +578 -0
- canfar_lab/studio_layer.py +968 -0
- canfar_lab/studio_paths.py +225 -0
- canfar_lab/studio_profile.py +177 -0
- canfar_lab/sync/__init__.py +15 -0
- canfar_lab/sync/arc_data.py +155 -0
- canfar_lab/sync/bus.py +236 -0
- canfar_lab/sync/catalog.py +221 -0
- canfar_lab/sync/clock.py +30 -0
- canfar_lab/sync/engine.py +512 -0
- canfar_lab/sync/watch.py +167 -0
- canfar_lab/ui.py +265 -0
- canfar_lab/utils/__init__.py +0 -0
- canfar_lab/utils/console.py +5 -0
- canfar_lab/utils/json_utils.py +141 -0
- canfar_lab/utils/subprocess.py +66 -0
- canfar_lab/utils/timing.py +61 -0
- canfar_lab/utils/toml_compat.py +12 -0
- canfar_lab/version.py +57 -0
- canfar_lab-0.7.0.dist-info/METADATA +156 -0
- canfar_lab-0.7.0.dist-info/RECORD +269 -0
- canfar_lab-0.7.0.dist-info/WHEEL +4 -0
- canfar_lab-0.7.0.dist-info/entry_points.txt +5 -0
- canfar_lab-0.7.0.dist-info/licenses/LICENSE +21 -0
- canfar_workload/__init__.py +30 -0
- canfar_workload/astro_data.py +561 -0
- canfar_workload/autoscaler.py +748 -0
- canfar_workload/canfar_ops.py +324 -0
- canfar_workload/cli.py +1095 -0
- canfar_workload/cluster.py +600 -0
- canfar_workload/dashboard.py +317 -0
- canfar_workload/executor.py +238 -0
- canfar_workload/manager_client.py +179 -0
- canfar_workload/mcp.py +626 -0
- canfar_workload/models.py +301 -0
- canfar_workload/preflight.py +196 -0
- canfar_workload/ray_cluster.py +119 -0
- canfar_workload/reconcile.py +211 -0
- canfar_workload/settings.py +96 -0
- canfar_workload/state_store.py +180 -0
- canfar_workload/worker_logs.py +45 -0
- canfar_workload/workers.py +231 -0
canfar_lab/__init__.py
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""canfar-lab — in-session workbench for AstroAI sessions on CANFAR."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
from canfar_lab.version import __version__, display_version, version_info
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def config_dir() -> Path:
|
|
11
|
+
"""Workbench config directory (~/.astroai/lab)."""
|
|
12
|
+
return Path.home() / ".astroai" / "lab"
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def saves_dir() -> Path:
|
|
16
|
+
"""Default directory for saved lockfile environments."""
|
|
17
|
+
return config_dir() / "saves"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
__all__ = ["__version__", "config_dir", "display_version", "saves_dir", "version_info"]
|
canfar_lab/__main__.py
ADDED
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
"""Install transports for agent plugins (MCP, rules, tools).
|
|
2
|
+
|
|
3
|
+
Plugin YAML under ``data/agent/plugins/*.yaml`` is the catalog. This module
|
|
4
|
+
applies those entries (bundled / github-rule / mcp-snippet / cli-tool) via
|
|
5
|
+
``_apply_addon``, also used by ``agent plugins install``.
|
|
6
|
+
|
|
7
|
+
Skills (SKILL.md) are not installed here — use ``npx skills``.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import shutil
|
|
13
|
+
from dataclasses import dataclass
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
from typing import Any
|
|
16
|
+
|
|
17
|
+
from canfar_lab.agent.agent_targets import (
|
|
18
|
+
MCP_TARGETS,
|
|
19
|
+
mcp_server_present,
|
|
20
|
+
mcp_target,
|
|
21
|
+
merge_mcp_server,
|
|
22
|
+
translate_mcp_entry,
|
|
23
|
+
)
|
|
24
|
+
from canfar_lab.agent.install import install_tool, tool_on_path
|
|
25
|
+
from canfar_lab.agent.upstream import (
|
|
26
|
+
_refresh_upstream_repo,
|
|
27
|
+
_upstream_cache_root,
|
|
28
|
+
)
|
|
29
|
+
from canfar_lab.errors import LabError
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@dataclass(frozen=True)
|
|
33
|
+
class AddonResult:
|
|
34
|
+
id: str
|
|
35
|
+
status: str
|
|
36
|
+
detail: str = ""
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def plugin_as_addon(plugin: dict[str, Any]) -> dict[str, Any]:
|
|
40
|
+
"""Map a plugin registry entry to the addon dict shape used by transports."""
|
|
41
|
+
install = dict(plugin.get("install") or {})
|
|
42
|
+
return {
|
|
43
|
+
"id": plugin["id"],
|
|
44
|
+
"kind": plugin["kind"],
|
|
45
|
+
"tags": plugin.get("tags", []),
|
|
46
|
+
"summary": plugin.get("summary", ""),
|
|
47
|
+
"homepage": plugin.get("homepage", ""),
|
|
48
|
+
"default": bool(plugin.get("default")),
|
|
49
|
+
"agents": list(plugin.get("agents", [])),
|
|
50
|
+
"install": install,
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def addon_installed(item: dict[str, Any], home: Path, agent: str | None = None) -> bool:
|
|
55
|
+
install = item.get("install") or {}
|
|
56
|
+
itype = install.get("type")
|
|
57
|
+
addon_id = item["id"]
|
|
58
|
+
|
|
59
|
+
if itype == "bundled":
|
|
60
|
+
if addon_id == "token-efficient":
|
|
61
|
+
return (home / ".cursor" / "rules" / "token-efficient.mdc").is_file()
|
|
62
|
+
if addon_id.startswith("mcp-"):
|
|
63
|
+
server = addon_id.removeprefix("mcp-")
|
|
64
|
+
return _mcp_server_present(home, server, agent=agent)
|
|
65
|
+
return False
|
|
66
|
+
|
|
67
|
+
if itype == "github-rule":
|
|
68
|
+
rule = Path(install.get("path", "")).name
|
|
69
|
+
return (home / ".cursor" / "rules" / rule).is_file()
|
|
70
|
+
|
|
71
|
+
if itype == "mcp-snippet":
|
|
72
|
+
server = install.get("server", "")
|
|
73
|
+
agents = [agent] if agent else list(item.get("agents") or [])
|
|
74
|
+
hosts = [a for a in agents if mcp_target(a)]
|
|
75
|
+
if not server or not hosts:
|
|
76
|
+
return False
|
|
77
|
+
return all(mcp_server_present(home, a, server) for a in hosts)
|
|
78
|
+
|
|
79
|
+
if itype == "cli-tool":
|
|
80
|
+
return tool_on_path(install.get("tool", addon_id))
|
|
81
|
+
|
|
82
|
+
return False
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def _mcp_server_present(home: Path, server: str, agent: str | None = None) -> bool:
|
|
86
|
+
"""True when the given agent (or every MCP host, if omitted) has ``server``."""
|
|
87
|
+
if not server:
|
|
88
|
+
return False
|
|
89
|
+
agents = [agent] if agent else list(MCP_TARGETS)
|
|
90
|
+
return all(mcp_server_present(home, a, server) for a in agents)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def add_addon(
|
|
94
|
+
addon_id: str,
|
|
95
|
+
*,
|
|
96
|
+
home: Path | None = None,
|
|
97
|
+
force: bool = False,
|
|
98
|
+
dry_run: bool = False,
|
|
99
|
+
) -> AddonResult:
|
|
100
|
+
from canfar_lab.agent.plugins import get_plugin
|
|
101
|
+
|
|
102
|
+
plugin = get_plugin(addon_id)
|
|
103
|
+
if plugin is None:
|
|
104
|
+
raise LabError(
|
|
105
|
+
f"Unknown addon: {addon_id}",
|
|
106
|
+
hint="canfar lab agent plugins list",
|
|
107
|
+
)
|
|
108
|
+
return _apply_addon(plugin_as_addon(plugin), home=home, force=force, dry_run=dry_run)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def _apply_addon(
|
|
112
|
+
item: dict[str, Any],
|
|
113
|
+
*,
|
|
114
|
+
home: Path | None = None,
|
|
115
|
+
force: bool = False,
|
|
116
|
+
dry_run: bool = False,
|
|
117
|
+
agent: str | None = None,
|
|
118
|
+
) -> AddonResult:
|
|
119
|
+
"""Apply one addon (plugin-as-addon dict) via its install transport.
|
|
120
|
+
|
|
121
|
+
``agent`` scopes mcp-snippet writes to one support-matrix agent; omit it
|
|
122
|
+
to apply every agent listed on the plugin.
|
|
123
|
+
"""
|
|
124
|
+
addon_id = item["id"]
|
|
125
|
+
home = home or Path.home()
|
|
126
|
+
install = item.get("install") or {}
|
|
127
|
+
itype = install.get("type")
|
|
128
|
+
|
|
129
|
+
if itype == "bundled":
|
|
130
|
+
return AddonResult(
|
|
131
|
+
addon_id,
|
|
132
|
+
"skipped",
|
|
133
|
+
install.get("note") or "bundled — run: canfar lab agent setup",
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
if not force and addon_installed(item, home, agent=agent):
|
|
137
|
+
return AddonResult(addon_id, "skipped", "already installed")
|
|
138
|
+
|
|
139
|
+
if itype == "github-rule":
|
|
140
|
+
return _install_github_rule(item, home=home, force=force, dry_run=dry_run)
|
|
141
|
+
|
|
142
|
+
if itype == "mcp-snippet":
|
|
143
|
+
return _install_mcp_snippet(item, home=home, force=force, dry_run=dry_run, agent=agent)
|
|
144
|
+
|
|
145
|
+
if itype == "cli-tool":
|
|
146
|
+
tool = install.get("tool")
|
|
147
|
+
if not tool:
|
|
148
|
+
raise LabError(f"Addon {addon_id} missing install.tool")
|
|
149
|
+
if dry_run:
|
|
150
|
+
return AddonResult(addon_id, "dry-run", f"would install CLI {tool}")
|
|
151
|
+
install_tool(tool, dry_run=False)
|
|
152
|
+
return AddonResult(addon_id, "installed", tool)
|
|
153
|
+
|
|
154
|
+
raise LabError(f"Addon {addon_id} has unsupported install type: {itype}")
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def _install_github_rule(
|
|
158
|
+
item: dict[str, Any],
|
|
159
|
+
*,
|
|
160
|
+
home: Path,
|
|
161
|
+
force: bool,
|
|
162
|
+
dry_run: bool,
|
|
163
|
+
) -> AddonResult:
|
|
164
|
+
install = item["install"]
|
|
165
|
+
repo = install["repo"]
|
|
166
|
+
path = install["path"]
|
|
167
|
+
if dry_run:
|
|
168
|
+
return AddonResult(item["id"], "dry-run", path)
|
|
169
|
+
|
|
170
|
+
cache_root = _upstream_cache_root(home, repo)
|
|
171
|
+
status, detail = _refresh_upstream_repo(cache_root, repo, path)
|
|
172
|
+
if status == "failed":
|
|
173
|
+
return AddonResult(item["id"], "failed", detail)
|
|
174
|
+
|
|
175
|
+
src = cache_root / path
|
|
176
|
+
if not src.is_file():
|
|
177
|
+
return AddonResult(item["id"], "failed", f"missing rule at {path}")
|
|
178
|
+
dst = home / ".cursor" / "rules" / src.name
|
|
179
|
+
if dst.is_file() and not force:
|
|
180
|
+
return AddonResult(item["id"], "skipped", "already installed")
|
|
181
|
+
dst.parent.mkdir(parents=True, exist_ok=True)
|
|
182
|
+
shutil.copy2(src, dst)
|
|
183
|
+
return AddonResult(item["id"], status, str(dst))
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def _install_mcp_snippet(
|
|
187
|
+
item: dict[str, Any],
|
|
188
|
+
*,
|
|
189
|
+
home: Path,
|
|
190
|
+
force: bool,
|
|
191
|
+
dry_run: bool,
|
|
192
|
+
agent: str | None = None,
|
|
193
|
+
) -> AddonResult:
|
|
194
|
+
install = item["install"]
|
|
195
|
+
server = install["server"]
|
|
196
|
+
cursor_cfg = install.get("cursor") or {}
|
|
197
|
+
opencode_cfg = install.get("opencode") or {}
|
|
198
|
+
agents = list(item.get("agents") or [])
|
|
199
|
+
if not agents:
|
|
200
|
+
raise LabError(
|
|
201
|
+
f"Addon {item['id']} mcp-snippet requires a non-empty agents support matrix",
|
|
202
|
+
hint="Declare agents: [cursor, ...] on the plugin YAML",
|
|
203
|
+
)
|
|
204
|
+
if agent is not None:
|
|
205
|
+
if agent not in agents:
|
|
206
|
+
return AddonResult(
|
|
207
|
+
item["id"], "skipped", f"{agent} not in support matrix ({', '.join(agents)})"
|
|
208
|
+
)
|
|
209
|
+
agents = [agent]
|
|
210
|
+
|
|
211
|
+
if dry_run:
|
|
212
|
+
return AddonResult(item["id"], "dry-run", f"mcp:{server} → {', '.join(agents)}")
|
|
213
|
+
|
|
214
|
+
written: list[str] = []
|
|
215
|
+
for ag in agents:
|
|
216
|
+
if mcp_target(ag) is None:
|
|
217
|
+
continue
|
|
218
|
+
entry = (
|
|
219
|
+
opencode_cfg
|
|
220
|
+
if ag == "opencode" and opencode_cfg
|
|
221
|
+
else translate_mcp_entry(ag, cursor_cfg)
|
|
222
|
+
)
|
|
223
|
+
if not entry:
|
|
224
|
+
continue
|
|
225
|
+
if merge_mcp_server(home, ag, server, entry, force=force):
|
|
226
|
+
written.append(ag)
|
|
227
|
+
if not written and not force:
|
|
228
|
+
return AddonResult(item["id"], "skipped", f"mcp:{server} already present")
|
|
229
|
+
detail = f"mcp:{server}" + (f" → {', '.join(written)}" if written else "")
|
|
230
|
+
return AddonResult(item["id"], "installed", detail)
|