mirror of
https://github.com/langgenius/dify.git
synced 2026-07-21 10:38:32 +08:00
Co-authored-by: Joel <iamjoel007@gmail.com> Co-authored-by: zyssyz123 <916125788@qq.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: 林玮 (Jade Lin) <linw1995@icloud.com> Co-authored-by: 盐粒 Yanli <mail@yanli.one>
641 lines
24 KiB
Python
641 lines
24 KiB
Python
"""Typer entry point for the client-safe ``dify-agent`` console script.
|
|
|
|
The CLI supports explicit ``connect``, ``file``, and ``drive`` commands and
|
|
treats unknown bare commands as Agent Stub forwards. When the injected Agent
|
|
Stub environment variables are missing, that path intentionally surfaces a
|
|
clear missing-env error instead of Typer's generic unknown-command message. The
|
|
module depends only on client-safe code so importing the console entry point
|
|
does not pull in FastAPI, Redis, shellctl, or JWE runtime dependencies.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from functools import cache
|
|
from importlib import import_module
|
|
import sys
|
|
|
|
import click
|
|
import typer
|
|
from typer.main import get_command
|
|
|
|
from dify_agent.agent_stub._constants import AGENT_STUB_DRIVE_BASE_ENV_VAR, DEFAULT_AGENT_STUB_DRIVE_BASE
|
|
|
|
_CONFIG_MANIFEST_STDOUT_EXCLUDE = {
|
|
"skills": {"items": {"__all__": {"hash"}}},
|
|
"files": {"items": {"__all__": {"hash"}}},
|
|
}
|
|
|
|
|
|
app = typer.Typer(
|
|
add_completion=False,
|
|
help="Forward shell-visible dify-agent commands to the Dify Agent Stub server.",
|
|
no_args_is_help=True,
|
|
rich_markup_mode=None,
|
|
)
|
|
file_app = typer.Typer(help="Upload or download workflow files through the Agent Stub.", rich_markup_mode=None)
|
|
config_app = typer.Typer(
|
|
help="Inspect or update Agent Soul-backed config assets through the Agent Stub.",
|
|
rich_markup_mode=None,
|
|
)
|
|
config_skills_app = typer.Typer(help="Pull or update config skills through the Agent Stub.", rich_markup_mode=None)
|
|
config_files_app = typer.Typer(help="Pull or update config files through the Agent Stub.", rich_markup_mode=None)
|
|
config_skill_pull_alias_app = typer.Typer(help="Pull config skills through the Agent Stub.", rich_markup_mode=None)
|
|
config_file_pull_alias_app = typer.Typer(help="Pull config files through the Agent Stub.", rich_markup_mode=None)
|
|
config_env_app = typer.Typer(help="Update config env variables visible to the current run.", rich_markup_mode=None)
|
|
config_note_app = typer.Typer(help="Pull or update the current config note.", rich_markup_mode=None)
|
|
drive_app = typer.Typer(help="List, pull, or push agent drive files through the Agent Stub.", rich_markup_mode=None)
|
|
app.add_typer(file_app, name="file")
|
|
app.add_typer(config_app, name="config")
|
|
config_app.add_typer(config_skills_app, name="skills")
|
|
# Keep hidden singular aliases on separate pull-only apps. Reusing the plural
|
|
# apps here would also expose push/delete through `config skill|file ...`,
|
|
# which is intentionally not part of the compatibility surface.
|
|
config_app.add_typer(config_skill_pull_alias_app, name="skill", hidden=True)
|
|
config_app.add_typer(config_files_app, name="files")
|
|
config_app.add_typer(config_file_pull_alias_app, name="file", hidden=True)
|
|
config_app.add_typer(config_env_app, name="env")
|
|
config_app.add_typer(config_note_app, name="note")
|
|
app.add_typer(drive_app, name="drive")
|
|
_KNOWN_ROOT_COMMANDS = frozenset({"config", "connect", "drive", "file"})
|
|
|
|
|
|
@app.command(context_settings={"allow_extra_args": True, "ignore_unknown_options": True})
|
|
def connect(
|
|
json_output: bool = typer.Option(False, "--json", help="Emit the connection response as JSON."),
|
|
argv: list[str] = typer.Argument(default_factory=list, metavar="ARGV"),
|
|
) -> None:
|
|
"""Establish one Agent Stub connection using the current environment."""
|
|
_run_connect(argv=list(argv), json_output=json_output)
|
|
|
|
|
|
@file_app.command("upload")
|
|
def upload(path: str = typer.Argument(..., metavar="PATH")) -> None:
|
|
"""Upload one sandbox-local file as a ToolFile output reference."""
|
|
_run_file_upload(path=path)
|
|
|
|
|
|
@file_app.command("download")
|
|
def download(
|
|
transfer_method: str | None = typer.Argument(None, metavar="TRANSFER_METHOD"),
|
|
reference_or_url: str | None = typer.Argument(None, metavar="REFERENCE_OR_URL"),
|
|
mapping: str | None = typer.Option(None, "--mapping", help="Download one file from a mapping JSON object."),
|
|
local_dir: str | None = typer.Option(None, "--to", help="Local directory for the downloaded file."),
|
|
) -> None:
|
|
"""Download one workflow file mapping into the local sandbox directory."""
|
|
_run_file_download(
|
|
transfer_method=transfer_method,
|
|
reference_or_url=reference_or_url,
|
|
mapping=mapping,
|
|
local_dir=local_dir,
|
|
)
|
|
|
|
|
|
@config_app.command("manifest")
|
|
def config_manifest() -> None:
|
|
"""Show the current visible Agent config manifest as JSON."""
|
|
_run_config_manifest()
|
|
|
|
|
|
@config_skills_app.command("pull")
|
|
def config_skills_pull(
|
|
names: list[str] = typer.Argument(None, metavar="NAME"),
|
|
local_dir: str | None = typer.Option(None, "--to", help="Local directory for pulled config skills."),
|
|
json_output: bool = typer.Option(False, "--json", help="Emit the pull result as JSON."),
|
|
) -> None:
|
|
"""Pull one or all visible config skills into ./.dify_conf/skills by default."""
|
|
_run_config_skill_pull(names=names or None, local_dir=local_dir, json_output=json_output)
|
|
|
|
|
|
@config_skill_pull_alias_app.command("pull")
|
|
def config_skill_pull_alias(
|
|
names: list[str] = typer.Argument(None, metavar="NAME"),
|
|
local_dir: str | None = typer.Option(None, "--to", help="Local directory for pulled config skills."),
|
|
json_output: bool = typer.Option(False, "--json", help="Emit the pull result as JSON."),
|
|
) -> None:
|
|
"""Pull one or all visible config skills into ./.dify_conf/skills by default."""
|
|
_run_config_skill_pull(names=names or None, local_dir=local_dir, json_output=json_output)
|
|
|
|
|
|
@config_skills_app.command("push")
|
|
def config_skills_push(
|
|
paths: list[str] = typer.Argument(..., metavar="PATH..."),
|
|
) -> None:
|
|
"""Upload one or more local skill directories into the current config manifest.
|
|
|
|
Pass a directory such as ./skills/researcher that contains SKILL.md. Other files in that directory are
|
|
archived with the skill. Pushing a skill with an existing name replaces that config skill.
|
|
"""
|
|
_run_config_skills_push(paths=paths)
|
|
|
|
|
|
@config_skills_app.command("delete")
|
|
def config_skills_delete(
|
|
names: list[str] = typer.Argument(..., metavar="NAME"),
|
|
) -> None:
|
|
"""Delete one or more config skills by name without touching local directories."""
|
|
_run_config_skills_delete(names=names)
|
|
|
|
|
|
@config_files_app.command("pull")
|
|
def config_files_pull(
|
|
names: list[str] = typer.Argument(None, metavar="NAME"),
|
|
local_dir: str | None = typer.Option(None, "--to", help="Local directory for pulled config files."),
|
|
json_output: bool = typer.Option(False, "--json", help="Emit the pull result as JSON."),
|
|
) -> None:
|
|
"""Pull one or all visible config files into ./.dify_conf/files by default."""
|
|
_run_config_file_pull(names=names or None, local_dir=local_dir, json_output=json_output)
|
|
|
|
|
|
@config_file_pull_alias_app.command("pull")
|
|
def config_file_pull_alias(
|
|
names: list[str] = typer.Argument(None, metavar="NAME"),
|
|
local_dir: str | None = typer.Option(None, "--to", help="Local directory for pulled config files."),
|
|
json_output: bool = typer.Option(False, "--json", help="Emit the pull result as JSON."),
|
|
) -> None:
|
|
"""Pull one or all visible config files into ./.dify_conf/files by default."""
|
|
_run_config_file_pull(names=names or None, local_dir=local_dir, json_output=json_output)
|
|
|
|
|
|
@config_files_app.command("push")
|
|
def config_files_push(
|
|
paths: list[str] = typer.Argument(..., metavar="PATH..."),
|
|
) -> None:
|
|
"""Upload one or more local files into the current config manifest.
|
|
|
|
Pass one or more regular local files such as ./guide.md and ./policy.txt. To upload a folder, compress it
|
|
first and pass the archive file. The config file name is the local basename. Pushing a file with an existing config
|
|
name replaces that config file.
|
|
"""
|
|
_run_config_files_push(paths=paths)
|
|
|
|
|
|
@config_files_app.command("delete")
|
|
def config_files_delete(
|
|
names: list[str] = typer.Argument(..., metavar="NAME"),
|
|
) -> None:
|
|
"""Delete one or more config files by name without touching local files."""
|
|
_run_config_files_delete(names=names)
|
|
|
|
|
|
@config_env_app.command("push")
|
|
def config_env_push(
|
|
local_path: str = typer.Argument(..., metavar="PATH|-"),
|
|
) -> None:
|
|
"""Set or delete config env entries from one local dotenv file or stdin.
|
|
|
|
Pass dotenv text with lines like API_KEY=value and OLD_KEY=. KEY=value sets or updates an entry, KEY= deletes
|
|
it, and omitted keys are unchanged.
|
|
"""
|
|
_run_config_env_push(local_path=local_path)
|
|
|
|
|
|
@config_note_app.command("pull")
|
|
def config_note_pull(
|
|
local_path: str | None = typer.Option(None, "--to", help="Local markdown file path."),
|
|
) -> None:
|
|
"""Export the current config note into ./.dify_conf/note.md by default."""
|
|
_run_config_note_pull(local_path=local_path)
|
|
|
|
|
|
@config_note_app.command("push")
|
|
def config_note_push(
|
|
local_path: str | None = typer.Argument(None, metavar="[PATH|-]"),
|
|
) -> None:
|
|
"""Replace the current config note from one local text file or stdin.
|
|
|
|
Pass a plain text or Markdown note file, or use - to read the note from stdin. When PATH is omitted, the
|
|
command reads ./.dify_conf/note.md.
|
|
"""
|
|
_run_config_note_push(local_path=local_path)
|
|
|
|
|
|
@drive_app.command("list")
|
|
def drive_list(
|
|
path_prefix: str = typer.Argument("", metavar="REMOTE_PREFIX"),
|
|
json_output: bool = typer.Option(False, "--json", help="Emit the drive manifest as JSON."),
|
|
) -> None:
|
|
"""List drive files visible to the current sandbox execution."""
|
|
_run_drive_list(path_prefix=path_prefix, json_output=json_output)
|
|
|
|
|
|
@drive_app.command("pull")
|
|
def drive_pull(
|
|
targets: list[str] = typer.Argument(None, metavar="REMOTE"),
|
|
local_base: str | None = typer.Option(
|
|
None,
|
|
"--to",
|
|
help=(
|
|
f"Local base directory for pulled drive files. Defaults to ${AGENT_STUB_DRIVE_BASE_ENV_VAR} "
|
|
f"or {DEFAULT_AGENT_STUB_DRIVE_BASE}."
|
|
),
|
|
),
|
|
json_output: bool = typer.Option(False, "--json", help="Emit the pull result as JSON."),
|
|
) -> None:
|
|
"""Pull one or more drive keys/prefixes into one local directory tree.
|
|
|
|
Passing no ``TARGET`` preserves the historical whole-drive behavior by
|
|
pulling from the empty prefix.
|
|
"""
|
|
_run_drive_pull(targets=targets or None, local_base=local_base, json_output=json_output)
|
|
|
|
|
|
@drive_app.command("push")
|
|
def drive_push(
|
|
local_path: str = typer.Argument(..., metavar="LOCAL_PATH"),
|
|
drive_path: str = typer.Argument(..., metavar="REMOTE_PATH"),
|
|
kind: str | None = typer.Option(None, "--kind", help="Directory upload kind: skill or dir."),
|
|
json_output: bool = typer.Option(
|
|
False,
|
|
"--json",
|
|
help="Accepted for consistency; drive push output is already emitted as JSON.",
|
|
),
|
|
) -> None:
|
|
"""Upload one local file or directory into the agent drive."""
|
|
del json_output
|
|
_run_drive_push(local_path=local_path, drive_path=drive_path, kind=kind)
|
|
|
|
|
|
def main(argv: list[str] | None = None) -> None:
|
|
"""Run the ``dify-agent`` CLI with optional argv injection for tests."""
|
|
args = list(sys.argv[1:] if argv is None else argv)
|
|
if args[:1] == ["connect"] and not _is_help_request(args[1:]):
|
|
json_output, forwarded_args = _parse_connect_args(args[1:])
|
|
_run_connect(argv=forwarded_args, json_output=json_output)
|
|
return
|
|
json_output, forwarded_args = _extract_root_json_flag(args)
|
|
if _is_unknown_bare_command(forwarded_args):
|
|
if not _env_module().has_agent_stub_environment():
|
|
_show_root_help()
|
|
_run_connect(argv=forwarded_args, json_output=json_output)
|
|
return
|
|
app(prog_name="dify-agent", args=args)
|
|
|
|
|
|
def _extract_root_json_flag(argv: list[str]) -> tuple[bool, list[str]]:
|
|
if len(argv) >= 2 and argv[0] == "--json" and argv[1] not in _KNOWN_ROOT_COMMANDS:
|
|
return True, argv[1:]
|
|
return False, argv
|
|
|
|
|
|
def _is_unknown_bare_command(argv: list[str]) -> bool:
|
|
if not argv:
|
|
return False
|
|
first = argv[0]
|
|
return first not in _KNOWN_ROOT_COMMANDS and not first.startswith("-")
|
|
|
|
|
|
def _parse_connect_args(argv: list[str]) -> tuple[bool, list[str]]:
|
|
json_output = False
|
|
remaining = list(argv)
|
|
if remaining[:1] == ["--json"]:
|
|
json_output = True
|
|
remaining = remaining[1:]
|
|
if remaining[:1] == ["--"]:
|
|
remaining = remaining[1:]
|
|
return json_output, remaining
|
|
|
|
|
|
def _is_help_request(argv: list[str]) -> bool:
|
|
return any(value in {"--help", "-h"} for value in argv)
|
|
|
|
|
|
def _show_root_help() -> None:
|
|
"""Render root CLI guidance before reporting unknown-command env errors."""
|
|
command = get_command(app)
|
|
context = command.make_context("dify-agent", [], resilient_parsing=True)
|
|
typer.echo(command.get_help(context))
|
|
|
|
|
|
def render_agent_stub_cli_help(args: tuple[str, ...]) -> str:
|
|
"""Render Click help for one known ``dify-agent`` subcommand without executing a shell."""
|
|
command: click.Command = get_command(app)
|
|
parent_context: click.Context | None = None
|
|
command_path = ["dify-agent"]
|
|
for name in args:
|
|
if not isinstance(command, click.Group):
|
|
raise ValueError(f"dify-agent {' '.join(args)} is not a command group")
|
|
next_command = command.commands.get(name)
|
|
if next_command is None:
|
|
raise ValueError(f"unknown dify-agent command path: {' '.join(args)}")
|
|
current_context = click.Context(command, info_name=command_path[-1], parent=parent_context)
|
|
parent_context = current_context
|
|
command = next_command
|
|
command_path.append(name)
|
|
context = click.Context(command, info_name=command_path[-1], parent=parent_context)
|
|
return command.get_help(context).strip()
|
|
|
|
|
|
def _run_connect(*, argv: list[str], json_output: bool) -> None:
|
|
env_module = _env_module()
|
|
client_module = _client_module()
|
|
agent_stub_module = _agent_stub_module()
|
|
try:
|
|
response = agent_stub_module.connect_from_environment(argv=argv)
|
|
except env_module.MissingAgentStubEnvironmentError as exc:
|
|
typer.echo(str(exc), err=True)
|
|
raise SystemExit(2) from exc
|
|
except client_module.AgentStubClientError as exc:
|
|
typer.echo(str(exc), err=True)
|
|
raise SystemExit(1) from exc
|
|
|
|
if json_output:
|
|
typer.echo(response.model_dump_json())
|
|
return
|
|
typer.echo(f"connected {response.connection_id}")
|
|
|
|
|
|
def _run_file_upload(*, path: str) -> None:
|
|
env_module = _env_module()
|
|
client_module = _client_module()
|
|
files_module = _files_module()
|
|
try:
|
|
response = files_module.upload_file_from_environment(path=path)
|
|
except env_module.MissingAgentStubEnvironmentError as exc:
|
|
typer.echo(str(exc), err=True)
|
|
raise SystemExit(2) from exc
|
|
except client_module.AgentStubClientError as exc:
|
|
typer.echo(str(exc), err=True)
|
|
raise SystemExit(1) from exc
|
|
typer.echo(response.model_dump_json())
|
|
|
|
|
|
def _run_file_download(
|
|
*,
|
|
transfer_method: str | None,
|
|
reference_or_url: str | None,
|
|
mapping: str | None,
|
|
local_dir: str | None,
|
|
) -> None:
|
|
env_module = _env_module()
|
|
client_module = _client_module()
|
|
files_module = _files_module()
|
|
try:
|
|
response = files_module.download_file_from_environment(
|
|
transfer_method=transfer_method,
|
|
reference_or_url=reference_or_url,
|
|
mapping=mapping,
|
|
local_dir=local_dir,
|
|
)
|
|
except env_module.MissingAgentStubEnvironmentError as exc:
|
|
typer.echo(str(exc), err=True)
|
|
raise SystemExit(2) from exc
|
|
except client_module.AgentStubClientError as exc:
|
|
typer.echo(str(exc), err=True)
|
|
raise SystemExit(1) from exc
|
|
typer.echo(str(response.path))
|
|
|
|
|
|
def _run_config_manifest() -> None:
|
|
env_module = _env_module()
|
|
client_module = _client_module()
|
|
config_module = _config_module()
|
|
try:
|
|
response = config_module.manifest_from_environment()
|
|
except env_module.MissingAgentStubEnvironmentError as exc:
|
|
typer.echo(str(exc), err=True)
|
|
raise SystemExit(2) from exc
|
|
except client_module.AgentStubClientError as exc:
|
|
typer.echo(str(exc), err=True)
|
|
raise SystemExit(1) from exc
|
|
typer.echo(response.model_dump_json(exclude=_CONFIG_MANIFEST_STDOUT_EXCLUDE))
|
|
|
|
|
|
def _run_config_skill_pull(*, names: list[str] | None, local_dir: str | None, json_output: bool) -> None:
|
|
env_module = _env_module()
|
|
client_module = _client_module()
|
|
config_module = _config_module()
|
|
try:
|
|
response = config_module.pull_config_skills_from_environment(names=names, local_dir=local_dir)
|
|
except env_module.MissingAgentStubEnvironmentError as exc:
|
|
typer.echo(str(exc), err=True)
|
|
raise SystemExit(2) from exc
|
|
except client_module.AgentStubClientError as exc:
|
|
typer.echo(str(exc), err=True)
|
|
raise SystemExit(1) from exc
|
|
if json_output:
|
|
typer.echo(response.model_dump_json())
|
|
return
|
|
for index, item in enumerate(response.items):
|
|
if index:
|
|
typer.echo("")
|
|
typer.echo(item.directory_path)
|
|
typer.echo(item.skill_md, nl=False)
|
|
|
|
|
|
def _run_config_file_pull(*, names: list[str] | None, local_dir: str | None, json_output: bool) -> None:
|
|
env_module = _env_module()
|
|
client_module = _client_module()
|
|
config_module = _config_module()
|
|
try:
|
|
response = config_module.pull_config_files_from_environment(names=names, local_dir=local_dir)
|
|
except env_module.MissingAgentStubEnvironmentError as exc:
|
|
typer.echo(str(exc), err=True)
|
|
raise SystemExit(2) from exc
|
|
except client_module.AgentStubClientError as exc:
|
|
typer.echo(str(exc), err=True)
|
|
raise SystemExit(1) from exc
|
|
if json_output:
|
|
typer.echo(response.model_dump_json())
|
|
return
|
|
for item in response.items:
|
|
typer.echo(item.path)
|
|
|
|
|
|
def _run_config_note_pull(*, local_path: str | None) -> None:
|
|
env_module = _env_module()
|
|
client_module = _client_module()
|
|
config_module = _config_module()
|
|
try:
|
|
path = config_module.pull_config_note_from_environment(local_path=local_path)
|
|
except env_module.MissingAgentStubEnvironmentError as exc:
|
|
typer.echo(str(exc), err=True)
|
|
raise SystemExit(2) from exc
|
|
except client_module.AgentStubClientError as exc:
|
|
typer.echo(str(exc), err=True)
|
|
raise SystemExit(1) from exc
|
|
typer.echo(str(path))
|
|
|
|
|
|
def _run_config_note_push(*, local_path: str | None) -> None:
|
|
env_module = _env_module()
|
|
client_module = _client_module()
|
|
config_module = _config_module()
|
|
try:
|
|
response = config_module.push_config_note_from_environment(local_path=local_path)
|
|
except env_module.MissingAgentStubEnvironmentError as exc:
|
|
typer.echo(str(exc), err=True)
|
|
raise SystemExit(2) from exc
|
|
except client_module.AgentStubClientError as exc:
|
|
typer.echo(str(exc), err=True)
|
|
raise SystemExit(1) from exc
|
|
typer.echo(response.model_dump_json())
|
|
|
|
|
|
def _run_config_env_push(*, local_path: str) -> None:
|
|
env_module = _env_module()
|
|
client_module = _client_module()
|
|
config_module = _config_module()
|
|
try:
|
|
response = config_module.push_config_env_from_environment(local_path=local_path)
|
|
except env_module.MissingAgentStubEnvironmentError as exc:
|
|
typer.echo(str(exc), err=True)
|
|
raise SystemExit(2) from exc
|
|
except client_module.AgentStubClientError as exc:
|
|
typer.echo(str(exc), err=True)
|
|
raise SystemExit(1) from exc
|
|
typer.echo(response.model_dump_json())
|
|
|
|
|
|
def _run_config_files_push(*, paths: list[str]) -> None:
|
|
env_module = _env_module()
|
|
client_module = _client_module()
|
|
config_module = _config_module()
|
|
try:
|
|
response = config_module.push_config_files_from_environment(paths=paths)
|
|
except env_module.MissingAgentStubEnvironmentError as exc:
|
|
typer.echo(str(exc), err=True)
|
|
raise SystemExit(2) from exc
|
|
except client_module.AgentStubClientError as exc:
|
|
typer.echo(str(exc), err=True)
|
|
raise SystemExit(1) from exc
|
|
typer.echo(response.model_dump_json())
|
|
|
|
|
|
def _run_config_files_delete(*, names: list[str]) -> None:
|
|
env_module = _env_module()
|
|
client_module = _client_module()
|
|
config_module = _config_module()
|
|
try:
|
|
response = config_module.delete_config_files_from_environment(names=names)
|
|
except env_module.MissingAgentStubEnvironmentError as exc:
|
|
typer.echo(str(exc), err=True)
|
|
raise SystemExit(2) from exc
|
|
except client_module.AgentStubClientError as exc:
|
|
typer.echo(str(exc), err=True)
|
|
raise SystemExit(1) from exc
|
|
typer.echo(response.model_dump_json())
|
|
|
|
|
|
def _run_config_skills_push(*, paths: list[str]) -> None:
|
|
env_module = _env_module()
|
|
client_module = _client_module()
|
|
config_module = _config_module()
|
|
try:
|
|
response = config_module.push_config_skills_from_environment(paths=paths)
|
|
except env_module.MissingAgentStubEnvironmentError as exc:
|
|
typer.echo(str(exc), err=True)
|
|
raise SystemExit(2) from exc
|
|
except client_module.AgentStubClientError as exc:
|
|
typer.echo(str(exc), err=True)
|
|
raise SystemExit(1) from exc
|
|
typer.echo(response.model_dump_json())
|
|
|
|
|
|
def _run_config_skills_delete(*, names: list[str]) -> None:
|
|
env_module = _env_module()
|
|
client_module = _client_module()
|
|
config_module = _config_module()
|
|
try:
|
|
response = config_module.delete_config_skills_from_environment(names=names)
|
|
except env_module.MissingAgentStubEnvironmentError as exc:
|
|
typer.echo(str(exc), err=True)
|
|
raise SystemExit(2) from exc
|
|
except client_module.AgentStubClientError as exc:
|
|
typer.echo(str(exc), err=True)
|
|
raise SystemExit(1) from exc
|
|
typer.echo(response.model_dump_json())
|
|
|
|
|
|
def _run_drive_list(*, path_prefix: str, json_output: bool) -> None:
|
|
env_module = _env_module()
|
|
client_module = _client_module()
|
|
drive_module = _drive_module()
|
|
try:
|
|
response = drive_module.list_drive_manifest_from_environment(prefix=path_prefix)
|
|
except env_module.MissingAgentStubEnvironmentError as exc:
|
|
typer.echo(str(exc), err=True)
|
|
raise SystemExit(2) from exc
|
|
except client_module.AgentStubClientError as exc:
|
|
typer.echo(str(exc), err=True)
|
|
raise SystemExit(1) from exc
|
|
if json_output:
|
|
typer.echo(response.model_dump_json())
|
|
return
|
|
typer.echo(drive_module.format_drive_manifest(response))
|
|
|
|
|
|
def _run_drive_pull(*, targets: list[str] | None, local_base: str | None, json_output: bool) -> None:
|
|
env_module = _env_module()
|
|
client_module = _client_module()
|
|
drive_module = _drive_module()
|
|
try:
|
|
response = drive_module.pull_drive_from_environment(
|
|
targets=targets,
|
|
local_base=local_base or env_module.read_agent_stub_drive_base(),
|
|
)
|
|
except env_module.MissingAgentStubEnvironmentError as exc:
|
|
typer.echo(str(exc), err=True)
|
|
raise SystemExit(2) from exc
|
|
except client_module.AgentStubClientError as exc:
|
|
typer.echo(str(exc), err=True)
|
|
raise SystemExit(1) from exc
|
|
if json_output:
|
|
typer.echo(response.model_dump_json())
|
|
return
|
|
for item in response.items:
|
|
typer.echo(item.local_path)
|
|
|
|
|
|
def _run_drive_push(*, local_path: str, drive_path: str, kind: str | None) -> None:
|
|
env_module = _env_module()
|
|
client_module = _client_module()
|
|
drive_module = _drive_module()
|
|
try:
|
|
response = drive_module.push_drive_from_environment(
|
|
local_path=local_path,
|
|
drive_path=drive_path,
|
|
kind=kind,
|
|
)
|
|
except env_module.MissingAgentStubEnvironmentError as exc:
|
|
typer.echo(str(exc), err=True)
|
|
raise SystemExit(2) from exc
|
|
except client_module.AgentStubClientError as exc:
|
|
typer.echo(str(exc), err=True)
|
|
raise SystemExit(1) from exc
|
|
typer.echo(response.model_dump_json())
|
|
|
|
|
|
# Keep helper imports on demand so importing CLI/help stays free of server-side
|
|
# and unrelated heavy runtime dependencies.
|
|
@cache
|
|
def _agent_stub_module():
|
|
return import_module("dify_agent.agent_stub.cli._agent_stub")
|
|
|
|
|
|
@cache
|
|
def _config_module():
|
|
return import_module("dify_agent.agent_stub.cli._config")
|
|
|
|
|
|
@cache
|
|
def _drive_module():
|
|
return import_module("dify_agent.agent_stub.cli._drive")
|
|
|
|
|
|
@cache
|
|
def _files_module():
|
|
return import_module("dify_agent.agent_stub.cli._files")
|
|
|
|
|
|
@cache
|
|
def _env_module():
|
|
return import_module("dify_agent.agent_stub.cli._env")
|
|
|
|
|
|
@cache
|
|
def _client_module():
|
|
return import_module("dify_agent.agent_stub.client")
|
|
|
|
|
|
__all__ = ["app", "main"]
|