bcbd1bdb22
Integ / changes (push) Has been skipped
Pre-commit / pre-commit (push) Failing after 1s
CLI exit codes / changes (push) Has been skipped
Test (Install) / changes (push) Has been skipped
Test (Python) / changes (push) Has been skipped
Test (TypeScript) / changes (push) Has been skipped
CLI exit codes / cli-gate (push) Has been cancelled
Test (Install) / test-install-gate (push) Has been cancelled
Integ / integ-gate (push) Has been cancelled
Test (Python) / test-python-gate (push) Has been cancelled
Test (TypeScript) / test-typescript-gate (push) Has been cancelled
Test (Install) / python-minimal (3.12) (push) Has been cancelled
Test (Install) / python-minimal (3.11) (push) Has been cancelled
Test (Install) / python-extra (agno, mirage.agents.agno) (push) Has been cancelled
Test (Install) / python-extra (chroma, mirage.resource.chroma) (push) Has been cancelled
Test (Install) / python-extra (pdf, mirage.core.filetype.pdf) (push) Has been cancelled
Integ / integ (push) Has been cancelled
Integ / integ-database (push) Has been cancelled
Integ / integ-database-ts (push) Has been cancelled
Integ / integ-data (push) Has been cancelled
Integ / integ-ssh (push) Has been cancelled
Integ / integ-ssh-ts (push) Has been cancelled
Test (Python) / audit (push) Has been cancelled
Test (TypeScript) / test (push) Has been cancelled
Test (TypeScript) / python-fs-shim (push) Has been cancelled
CLI exit codes / Python CLI (push) Has been cancelled
CLI exit codes / TypeScript CLI (push) Has been cancelled
CLI exit codes / Cross-language snapshot interop (push) Has been cancelled
Test (Python) / test (push) Has been cancelled
Test (Python) / import-isolation (deepagents, openai, mirage.agents.openai_agents) (push) Has been cancelled
Test (Python) / import-isolation (deepagents, pydantic-ai, mirage.agents.pydantic_ai) (push) Has been cancelled
Integ / integ-ts (push) Has been cancelled
Integ / integ-fuse (push) Has been cancelled
Test (Install) / python-extra (databricks, mirage.resource.databricks_volume) (push) Has been cancelled
Test (Install) / python-extra (deepagents, mirage.agents.langchain) (push) Has been cancelled
Test (Install) / python-extra (email, mirage.resource.email) (push) Has been cancelled
Test (Install) / python-extra (fuse, mirage.fuse.mount) (push) Has been cancelled
Test (Install) / python-extra (hdf5, mirage.core.filetype.hdf5) (push) Has been cancelled
Test (Install) / python-extra (hf, mirage.resource.hf_buckets) (push) Has been cancelled
Test (Install) / python-extra (lancedb, mirage.resource.lancedb) (push) Has been cancelled
Test (Install) / python-extra (langfuse, mirage.resource.langfuse) (push) Has been cancelled
Test (Install) / python-extra (mongodb, mirage.resource.mongodb) (push) Has been cancelled
Test (Install) / python-extra (nextcloud, mirage.resource.nextcloud) (push) Has been cancelled
Test (Install) / python-extra (openai, mirage.agents.openai_agents) (push) Has been cancelled
Test (Install) / python-extra (openhands, mirage.agents.openhands, 3.12) (push) Has been cancelled
Test (Install) / python-extra (parquet, mirage.core.filetype.parquet) (push) Has been cancelled
Test (Install) / python-extra (postgres, mirage.resource.postgres) (push) Has been cancelled
Test (Install) / python-extra (pydantic-ai, mirage.agents.pydantic_ai) (push) Has been cancelled
Test (Install) / python-extra (qdrant, mirage.resource.qdrant) (push) Has been cancelled
Test (Install) / python-extra (redis, mirage.resource.redis) (push) Has been cancelled
Test (Install) / python-extra (s3, mirage.resource.s3) (push) Has been cancelled
Test (Install) / python-extra (ssh, mirage.resource.ssh) (push) Has been cancelled
Test (Install) / ts-minimal (push) Has been cancelled
201 lines
6.8 KiB
Python
201 lines
6.8 KiB
Python
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
|
|
|
|
from dataclasses import dataclass
|
|
|
|
from mirage.commands.config import RegisteredCommand
|
|
from mirage.commands.spec import SPECS, CommandSpec
|
|
from mirage.io import IOResult
|
|
from mirage.io.types import ByteSource
|
|
from mirage.workspace.mount.mount import MountEntry
|
|
from mirage.workspace.mount.registry import DEV_PREFIX, MountRegistry
|
|
from mirage.workspace.session import Session
|
|
from mirage.workspace.types import ExecutionNode
|
|
|
|
|
|
@dataclass
|
|
class _ManHit:
|
|
mount: MountEntry
|
|
cmd: RegisteredCommand
|
|
is_general: bool
|
|
|
|
|
|
def _collect_man_hits(name: str, registry: MountRegistry) -> list[_ManHit]:
|
|
hits: list[_ManHit] = []
|
|
for mount in registry.mounts():
|
|
if mount.prefix == DEV_PREFIX:
|
|
continue
|
|
cmd = mount.resolve_command(name)
|
|
if cmd is None:
|
|
continue
|
|
hits.append(
|
|
_ManHit(mount=mount,
|
|
cmd=cmd,
|
|
is_general=mount.is_general_command(name)))
|
|
return hits
|
|
|
|
|
|
def _render_options_table(spec: CommandSpec) -> list[str]:
|
|
if not spec.options:
|
|
return []
|
|
lines: list[str] = []
|
|
lines.append("## OPTIONS")
|
|
lines.append("")
|
|
lines.append("| short | long | value | description |")
|
|
lines.append("| ----- | ---- | ----- | ----------- |")
|
|
for opt in spec.options:
|
|
short = opt.short if opt.short is not None else ""
|
|
long = opt.long if opt.long is not None else ""
|
|
desc = opt.description if opt.description is not None else ""
|
|
lines.append(f"| {short} | {long} | {opt.value_kind.value} | {desc} |")
|
|
lines.append("")
|
|
return lines
|
|
|
|
|
|
def _render_man_entry(name: str, hits: list[_ManHit]) -> str:
|
|
first = hits[0]
|
|
spec = first.cmd.spec
|
|
lines: list[str] = []
|
|
lines.append(f"# {name}")
|
|
lines.append("")
|
|
lines.append(spec.description if spec.
|
|
description is not None else "(no description)")
|
|
lines.append("")
|
|
lines.extend(_render_options_table(spec))
|
|
lines.append("## RESOURCES")
|
|
lines.append("")
|
|
seen: set[str] = set()
|
|
has_general = False
|
|
rows: list[str] = []
|
|
for h in hits:
|
|
if h.is_general:
|
|
has_general = True
|
|
continue
|
|
kind = h.mount.resource.name
|
|
filetype = h.cmd.filetype
|
|
key = f"{kind}\x00{filetype or ''}"
|
|
if key in seen:
|
|
continue
|
|
seen.add(key)
|
|
if filetype is not None:
|
|
rows.append(f"- {kind} (filetype: {filetype})")
|
|
else:
|
|
rows.append(f"- {kind}")
|
|
rows.sort()
|
|
if has_general:
|
|
lines.append("- general")
|
|
for r in rows:
|
|
lines.append(r)
|
|
return "\n".join(lines) + "\n"
|
|
|
|
|
|
_SHELL_BUILTIN_MAN: dict[str, str] = {
|
|
"bash": "bash",
|
|
"sh": "bash",
|
|
}
|
|
|
|
|
|
def _render_shell_builtin_man(name: str, spec: CommandSpec) -> str:
|
|
lines: list[str] = []
|
|
lines.append(f"# {name}")
|
|
lines.append("")
|
|
lines.append(spec.description if spec.
|
|
description is not None else "(no description)")
|
|
lines.append("")
|
|
lines.extend(_render_options_table(spec))
|
|
lines.append("## RESOURCES")
|
|
lines.append("")
|
|
lines.append("- shell builtin")
|
|
return "\n".join(lines) + "\n"
|
|
|
|
|
|
def _render_man_index(session: Session, registry: MountRegistry) -> str:
|
|
by_kind: dict[str, MountEntry] = {}
|
|
for m in registry.mounts():
|
|
if m.prefix == DEV_PREFIX:
|
|
continue
|
|
if m.resource.name not in by_kind:
|
|
by_kind[m.resource.name] = m
|
|
try:
|
|
cwd_mount: MountEntry | None = registry.mount_for(session.cwd)
|
|
except ValueError:
|
|
cwd_mount = None
|
|
cwd_kind: str | None = None
|
|
if cwd_mount is not None and cwd_mount.prefix != DEV_PREFIX:
|
|
cwd_kind = cwd_mount.resource.name
|
|
|
|
kinds = sorted(by_kind.keys())
|
|
ordered: list[str] = []
|
|
if cwd_kind is not None and cwd_kind in by_kind:
|
|
ordered.append(cwd_kind)
|
|
for k in kinds:
|
|
if k == cwd_kind:
|
|
continue
|
|
ordered.append(k)
|
|
|
|
lines: list[str] = []
|
|
general_seen: dict[str, RegisteredCommand] = {}
|
|
for kind in ordered:
|
|
m = by_kind[kind]
|
|
lines.append(f"# {kind}")
|
|
lines.append("")
|
|
all_cmds = m.all_commands()
|
|
resource_cmds = sorted(
|
|
(c for c in all_cmds if not m.is_general_command(c.name)),
|
|
key=lambda c: c.name,
|
|
)
|
|
for cmd in resource_cmds:
|
|
desc = (cmd.spec.description if cmd.spec.description is not None
|
|
else "(no description)")
|
|
lines.append(f"- {cmd.name} \u2014 {desc}")
|
|
for cmd in all_cmds:
|
|
if (m.is_general_command(cmd.name)
|
|
and cmd.name not in general_seen):
|
|
general_seen[cmd.name] = cmd
|
|
lines.append("")
|
|
lines.append("# general")
|
|
lines.append("")
|
|
for name in sorted(general_seen):
|
|
cmd = general_seen[name]
|
|
desc = (cmd.spec.description
|
|
if cmd.spec.description is not None else "(no description)")
|
|
lines.append(f"- {name} \u2014 {desc}")
|
|
return "\n".join(lines) + "\n"
|
|
|
|
|
|
async def handle_man(
|
|
args: list[str],
|
|
session: Session,
|
|
registry: MountRegistry,
|
|
) -> tuple[ByteSource | None, IOResult, ExecutionNode]:
|
|
if not args:
|
|
out = _render_man_index(session, registry).encode()
|
|
return out, IOResult(), ExecutionNode(command="man", exit_code=0)
|
|
name = args[0]
|
|
hits = _collect_man_hits(name, registry)
|
|
if not hits:
|
|
spec_key = _SHELL_BUILTIN_MAN.get(name)
|
|
spec = SPECS.get(spec_key) if spec_key is not None else None
|
|
if spec is not None:
|
|
out = _render_shell_builtin_man(name, spec).encode()
|
|
return out, IOResult(), ExecutionNode(command=f"man {name}",
|
|
exit_code=0)
|
|
err = f"man: no entry for {name}\n".encode()
|
|
return None, IOResult(exit_code=1,
|
|
stderr=err), ExecutionNode(command=f"man {name}",
|
|
exit_code=1,
|
|
stderr=err)
|
|
out = _render_man_entry(name, hits).encode()
|
|
return out, IOResult(), ExecutionNode(command=f"man {name}", exit_code=0)
|