1"""

2 AgentOS v1.7.1 CLI — System layer + Desktop client: file ops, shell, browser, visual approval, native desktop shell。

3"""

4

5 from __future__ import annotations

6

7 import asyncio

8 import os

9 import sys

10

11 from agentos.llm.factory import create_provider

12 from agentos.llm.base import Tool, ToolParameter

13 from agentos.agent.tool_agent import ToolAgent, ToolExecutor, AgentConfig, MockLLMProvider

14 from agentos.cli.errors import (

15 no_provider_configured,

16 single_provider_failed,

17 no_task_provided,

18 welcome,

19)

20

21

22 def _build_executor() -> ToolExecutor:

23 executor = ToolExecutor()

24

25 # Shell tool

26 executor.register(

27 Tool.from_function(

28 name="run_shell",

29 description="Execute a shell command. Commands run in a sandboxed temporary directory.",

30 parameters={

31 "command": ToolParameter(

32 type="string",

33 description="The shell command to execute.",

34 ),

35 },

36 ),

37 lambda command: _run_shell_unsafe(command),

38 )

39

40 # File tools

41 executor.register(

42 Tool.from_function(

43 name="read_file",

44 description="Read contents of a file at the given path.",

45 parameters={

46 "file_path": ToolParameter(

47 type="string",

48 description="Absolute path to the file.",

49 ),

50 },

51 ),

52 lambda file_path: _read_file(file_path),

53 )

54

55 executor.register(

56 Tool.from_function(

57 name="list_directory",

58 description="List files and subdirectories in a directory.",

59 parameters={

60 "path": ToolParameter(

61 type="string",

62 description="Absolute path to the directory.",

63 ),

64 },

65 ),

66 lambda path: _list_directory(path),

67 )

68

69 executor.register(

70 Tool.from_function(

71 name="write_file",

72 description="Write text content to a file.",

73 parameters={

74 "file_path": ToolParameter(

75 type="string",

76 description="Absolute path to write to.",

77 ),

78 "content": ToolParameter(

79 type="string",

80 description="Text content to write.",

81 ),

82 },

83 ),

84 lambda file_path, content: _write_file(file_path, content),

85 )

86

87 return executor

88

89

90 def _run_shell_unsafe(command: str) -> str:

91 import subprocess

92 import tempfile

93 try:

94 with tempfile.TemporaryDirectory() as td:

95 result = subprocess.run(

96 command, shell=True, capture_output=True, text=True,

97 timeout=30, cwd=td,

98 )

99 out = result.stdout.strip()

100 err = result.stderr.strip()

101 if err:

102 return f"stdout:\n{out}\n\nstderr:\n{err}"

103 return out or "(no output)"

104 except subprocess.TimeoutExpired:

105 return "Error: command timed out (30s)"

106 except Exception as e:

107 return f"Error: {e}"

108

109

110 def _read_file(file_path: str) -> str:

111 try:

112 with open(file_path) as f:

113 return f.read()

114 except Exception as e:

115 return f"Error: {e}"

116

117

118 def _list_directory(path: str) -> str:

119 try:

120 entries = os.listdir(path)

121 return "\n".join(sorted(entries)) or "(empty)"

122 except Exception as e:

123 return f"Error: {e}"

124

125

126 def _write_file(file_path: str, content: str) -> str:

127 try:

128 os.makedirs(os.path.dirname(file_path) or ".", exist_ok=True)

129 with open(file_path, "w") as f:

130 f.write(content)

131 return f"Written {len(content)} bytes to {file_path}"

132 except Exception as e:

133 return f"Error: {e}"

134

135

136 def _build_agent(provider_name: str = "", verbose: bool = False) -> ToolAgent:

137 if provider_name:

138 provider = create_provider(provider_name)

139 elif os.getenv("OPENAI_API_KEY"):

140 provider = create_provider("openai")

141 provider_name = "openai"

142 elif os.getenv("DEEPSEEK_API_KEY"):

143 provider = create_provider("deepseek")

144 provider_name = "deepseek"

145 elif os.getenv("ANTHROPIC_API_KEY"):

146 provider = create_provider("anthropic")

147 provider_name = "anthropic"

148 else:

149 welcome()

150 no_provider_configured()

151

152 config = AgentConfig(verbose=verbose)

153 executor = _build_executor()

154

155 return ToolAgent(

156 provider=provider,

157 tool_executor=executor,

158 config=config,

159 system_prompt=(

160 "你是 AgentOS 智能助手。你可以使用以下工具完成任务：\n"

161 "- run_shell: 执行 Shell 命令\n"

162 "- read_file: 读取文件内容\n"

163 "- write_file: 写入文本到文件\n"

164 "- list_directory: 列出目录内容\n\n"

165 "当需要操作文件或执行命令时调用对应工具。"

166 "获得足够信息后直接回答，不要再调用工具。"

167 "使用中文回复。"

168 ),

169 )

170

171

172 def _run_hello():

173 """一键 hello world 体验 — 无需配置，始终可用。"""

174 from agentos import __version__

175 import time

176

177 print(f" \033[36mNexus AgentOS\033[0m v{__version__}")

178 print(f" \033[2m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\033[0m")

179 print()

180

181 # Step 1: check provider

182 provider_label = "Mock（演示模式）"

183 provider_color = "\033[33m"

184 if os.environ.get("OPENAI_API_KEY"):

185 provider_label = "OpenAI (gpt-4o-mini)"

186 provider_color = "\033[32m"

187 elif os.environ.get("DEEPSEEK_API_KEY"):

188 provider_label = "DeepSeek (deepseek-chat)"

189 provider_color = "\033[32m"

190 elif os.environ.get("ANTHROPIC_API_KEY"):

191 provider_label = "Anthropic (claude-sonnet-4)"

192 provider_color = "\033[32m"

193

194 steps = [

195 ("检测环境", f"{provider_color}{provider_label}\033[0m"),

196 ("核心引擎", "\033[32mToolAgent 多步推理循环\033[0m"),

197 ("工具系统", "\033[32mShell / 文件读写 / 目录浏览\033[0m"),

198 ("安全护栏", "\033[32mGuardrails PII注入检测\033[0m"),

199 ("可观测性", "\033[32mTracker + Dashboard SSE\033[0m"),

200 ("MCP 协议", "\033[32mstdio JSON-RPC 2.0\033[0m"),

201 ("RAG 管道", "\033[32mChroma/FAISS 向量检索\033[0m"),

202 ("企业特性", "\033[32mRBAC/多租户/审计/API Key\033[0m"),

203 ]

204

205 for label, status in steps:

206 time.sleep(0.12)

207 print(f" \033[2m▸\033[0m {label:<12s} {status}")

208

209 time.sleep(0.3)

210 print()

211 print(f" \033[1m一切就绪。\033[0m")

212 print()

213

214 if os.environ.get("OPENAI_API_KEY") or os.environ.get("DEEPSEEK_API_KEY") or os.environ.get("ANTHROPIC_API_KEY"):

215 print(f" 快速开始：")

216 print(f" \033[32magentos\033[0m \"用一句话解释什么是递归\"")

217 print(f" \033[32magentos demo\033[0m")

218 else:

219 print(f" 下一步（30 秒配置）：")

220 print(f" \033[32magentos init\033[0m 终端交互式配置")

221 print(f" \033[32magentos config-panel\033[0m 浏览器图形界面")

222 print()

223

224

225 def _run_file_demo(verbose: bool):

226 """文件操作演示 — 创建、读取、列表。"""

227 from agentos.agent.tool_agent import ToolAgent, ToolExecutor, AgentConfig

228

229 executor = ToolExecutor()

230

231 def write_file(path: str, content: str) -> str:

232 import os as _os

233 try:

234 _os.makedirs(_os.path.dirname(path) or ".", exist_ok=True)

235 with open(path, "w") as f:

236 f.write(content)

237 return f"已写入 {path} ({len(content)} 字节)"

238 except Exception as e:

239 return f"写入失败: {e}"

240

241 def read_file(path: str) -> str:

242 try:

243 with open(path) as f:

244 return f.read()

245 except Exception as e:

246 return f"读取失败: {e}"

247

248 def list_dir(path: str) -> str:

249 import os as _os

250 try:

251 return "\n".join(sorted(_os.listdir(path))) or "(空)"

252 except Exception as e:

253 return f"列出失败: {e}"

254

255 executor.register(

256 Tool.from_function("write_file", "写入文本到文件", {

257 "path": ToolParameter(type="string", description="文件路径"),

258 "content": ToolParameter(type="string", description="文件内容"),

259 }),

260 write_file,

261 )

262 executor.register(

263 Tool.from_function("read_file", "读取文件内容", {

264 "path": ToolParameter(type="string", description="文件路径"),

265 }),

266 read_file,

267 )

268 executor.register(

269 Tool.from_function("list_dir", "列出目录内容", {

270 "path": ToolParameter(type="string", description="目录路径"),

271 }),

272 list_dir,

273 )

274

275 if os.getenv("OPENAI_API_KEY"):

276 provider = create_provider("openai")

277 elif os.getenv("DEEPSEEK_API_KEY"):

278 provider = create_provider("deepseek")

279 else:

280 print("\n 文件操作演示需要 API Key（Mock 不支持动态文件操作）。")

281 print(" 运行 agentos init 配置后重试。\n")

282 return

283

284 agent = ToolAgent(

285 provider=provider,

286 tool_executor=executor,

287 config=AgentConfig(verbose=verbose, temperature=0.0),

288 system_prompt="你是文件操作助手。用工具完成任务后用中文汇报结果。",

289 )

290

291 demo_dir = "/tmp/agentos_file_demo"

292 print(f"\n 演示目录: {demo_dir}")

293 print()

294

295 result = agent.run(f"在 {demo_dir} 下创建 hello.txt 写入 'Hello from AgentOS!'，列出目录内容，再读取 hello.txt 确认。")

296 print(f"\n 耗时: {result.total_duration_ms/1000:.1f}s | 步数: {result.total_steps}")

297 print(f" Token: {result.total_tokens} | 费用: ${result.total_cost_usd:.4f}")

298 print(f" 结果: {result.final_answer}")

299

300

301 def main():

302 args = sys.argv[1:]

303 verbose = "--verbose" in args or "-v" in args

304 args = [a for a in args if a not in ("--verbose", "-v")]

305

306 if not args or args[0] in ("help", "--help", "-h"):

307 from agentos import __version__

308 from agentos.cli.init import config_status_text

309 print(f"AgentOS v{__version__} — Production Agent Framework CLI\n")

310 print(f"Provider: {config_status_text()}\n")

311 print("Usage:")

312 print(" agentos init Interactive setup wizard (recommended)")

313 print(" agentos hello One-step health check & quick intro")

314 print(" agentos config-panel Open web config panel in browser")

315 print(" agentos <task> Run a task with the autonomous agent")

316 print(" agentos run <task> Same as above")

317 print(" agentos demo Run interactive demo (weather/stock/files)")

318 print(" agentos serve Start API server (port 8080)")

319 print(" agentos daemon <cmd> Server daemon: start|stop|status|restart|run")

320 print(" agentos version Show version")

321 print(" agentos skills List agent marketplace skills")

322 print(" agentos docs [output-dir] Generate API reference docs (→ docs/api/)")

323 print(" agentos dashboard Open web trace dashboard (port 18500)")

324 print(" agentos mcp-server Start MCP server (stdio) for Claude Desktop etc.")

325 print(" agentos desktop Launch web desktop client (port 19999)")

326 print(" agentos desktop-shell Launch native desktop shell (pywebview)")

327 print(" agentos enterprise <cmd> Enterprise features: api-key, tenant, audit")

328 print(" agentos marketplace <cmd> Skill marketplace: search|install|list|update|uninstall|stats")

329 print(" agentos rollback <version> Rollback to a previous version (--list|--verify|--prune)")

330 print("\nOptions:")

331 print(" -v, --verbose Show agent step details")

332 print(" --provider <name> Force provider: openai|deepseek|anthropic")

333 print("\nProvider (auto-detect via env vars):")

334 print(" OPENAI_API_KEY → OpenAI (gpt-4o-mini)")

335 print(" DEEPSEEK_API_KEY → DeepSeek (deepseek-chat)")

336 print(" ANTHROPIC_API_KEY → Anthropic (claude-sonnet-4)")

337 print(" (none set) → Mock demo mode — run 'agentos init' to configure")

338 print("\nExamples:")

339 print(" agentos hello # 30s quick tour")

340 print(" agentos init # 1-minute setup wizard")

341 print(" agentos \"列出当前目录的文件\"")

342 print(" agentos \"创建一个 hello.py 打印 Hello World\"")

343 print(" agentos demo")

344 sys.exit(0)

345

346 cmd = args[0]

347 if cmd == "init":

348 from agentos.cli.init import init_cli

349 init_cli(args)

350 return

351

352 if cmd == "config-panel":

353 from agentos.cli.config_panel import start_panel

354 start_panel()

355 return

356

357 if cmd == "status":

358 from agentos.cli.init import _detect_current_config, config_status_text

359 print(f"Provider: {config_status_text()}\n")

360 config = _detect_current_config()

361 for name, info in config["providers"].items():

362 from agentos.cli.init import PROVIDERS

363 p = PROVIDERS[name]

364 status_icon = "✅" if info["env_set"] else "⬜"

365 key_info = info.get("key_preview", "未配置") or "未配置"

366 in_config = " (配置文件)" if info.get("in_config") else ""

367 print(f" {status_icon} {p['label']:20s} {key_info:25s}{in_config}")

368 return

369

370 if cmd == "version":

371 from agentos import __version__

372 print(f"AgentOS v{__version__}")

373 return

374

375 if cmd == "skills":

376 from agentos.agents.market import AgentMarket

377 market = AgentMarket()

378 stats = market.stats()

379 print(f"Agent Skill Market: {stats['total']} skills\n")

380 for cat, count in stats["by_category"].items():

381 skills = market.list_by_category(cat)

382 print(f" [{cat}] ({count})")

383 for s in skills:

384 print(f" {s.name}: {s.description}")

385 return

386

387 if cmd == "docs":

388 from agentos.docs.generator import generate_api_docs, generate_quickstart

389 import os

390 if not os.path.isdir("agentos"):

391 print("Error: run 'agentos docs' from the agentos project root directory")

392 sys.exit(1)

393 output_dir = args[1] if len(args) > 1 else "docs"

394 os.makedirs(output_dir, exist_ok=True)

395 api_path = os.path.join(output_dir, "api_reference.md")

396 qs_path = os.path.join(output_dir, "quickstart.md")

397 md = generate_api_docs("agentos", api_path)

398 generate_quickstart(qs_path)

399 module_count = len([d for d in os.listdir("agentos") if os.path.isdir(os.path.join("agentos", d))])

400 print(f"Generated API docs: {api_path} ({len(md.splitlines())} lines)")

401 print(f"Generated Quickstart: {qs_path}")

402 print(f"Scanned ~{module_count} source modules")

403 return

404

405 if cmd == "dashboard":

406 from agentos.dashboard.server import start_dashboard

407 print("Starting AgentOS Dashboard...")

408 start_dashboard()

409 return

410

411 if cmd == "mcp-server":

412 from agentos.mcp.server import start_mcp_server

413 port = 0

414 for i, a in enumerate(args[1:]):

415 if a == "--port" and i + 2 < len(args):

416 port = int(args[i + 2])

417 start_mcp_server(port=port)

418 return

419

420 if cmd == "desktop":

421 from agentos.desktop.server import launch_desktop

422 host = "0.0.0.0"

423 port = 19999

424 mode = "dev"

425 for i, a in enumerate(args[1:]):

426 if a == "--host" and i + 2 < len(args):

427 host = args[i + 2]

428 elif a == "--port" and i + 2 < len(args):

429 port = int(args[i + 2])

430 elif a == "--safe":

431 mode = "safe"

432 launch_desktop(host=host, port=port, mode=mode)

433 return

434

435 if cmd == "desktop-shell":

436 from agentos.desktop.shell import main as shell_main

437 sys.argv = [sys.argv[0]] + args[1:]

438 shell_main()

439 return

440

441 if cmd == "enterprise":

442 _run_enterprise(args[1:])

443 return

444

445 if cmd == "marketplace":

446 _run_marketplace(args[1:])

447 return

448

449 if cmd == "rollback":

450 from agentos.cli.rollback import rollback_cli

451 sys.exit(rollback_cli(args[1:]))

452

453 if cmd == "serve":

454 host = "0.0.0.0"

455 port = 8080

456 for i, a in enumerate(args[1:]):

457 if a == "--host" and i + 2 < len(args):

458 host = args[i + 2]

459 elif a == "--port" and i + 2 < len(args):

460 port = int(args[i + 2])

461 from agentos.api.server import AgentAPI

462 from agentos.core.loop import AgentLoop, LoopConfig

463 from agentos.core.context import ContextManager

464 from agentos.tools.registry import ToolRegistry

465 from agentos.models.router import ModelRouter, RECOMMENDED_CONFIG

466 from agentos.tools.code_agent import CodeAgentTool, ShellTool

467 from agentos.tools.file_tools import ReadFileTool, WriteFileTool, ListDirectoryTool

468 from agentos.tools.web_tools import WebFetchTool

469 registry = ToolRegistry()

470 registry.register_many([ReadFileTool(), WriteFileTool(), ListDirectoryTool(), CodeAgentTool(), ShellTool(), WebFetchTool()])

471 ctx = ContextManager(system_prompt="AgentOS API Server v1.0")

472 router = ModelRouter(RECOMMENDED_CONFIG)

473 loop = AgentLoop(model_router=router, tool_registry=registry, context_manager=ctx)

474 api = AgentAPI(loop)

475 print(f"AgentOS API starting on http://{host}:{port}")

476 api.serve(host=host, port=port)

477 return

478

479 if cmd == "daemon":

480 from agentos.server.daemon import daemon_main

481 sys.exit(daemon_main(args[1:]))

482

483 if cmd == "hello":

484 _run_hello()

485 return

486

487 if cmd == "demo":

488 print("=" * 60)

489 print(" AgentOS — Interactive Demo")

490 print("=" * 60)

491 print()

492 print(" 选择演示场景：")

493 print(" [1] 天气助手（默认）")

494 print(" [2] 文件操作")

495 print(" [3] 健康自检")

496 print()

497

498 try:

499 choice = input(" 请输入数字 (1-3) [1]: ").strip() or "1"

500 except (EOFError, KeyboardInterrupt):

501 choice = "1"

502

503 if choice == "3":

504 _run_hello()

505 elif choice == "2":

506 _run_file_demo(verbose)

507 else:

508 _run_demo(verbose)

509 return

510

511 # Run task

512 task_start = 1 if cmd == "run" else 0

513 task = " ".join(args[task_start:])

514 if not task.strip():

515 no_task_provided()

516

517 agent = _build_agent(verbose=verbose)

518 # 自动记录 tracker

519 from agentos.dashboard.tracker import Tracker

520 import uuid, time

521 tracker = Tracker.get()

522 session_id = f"run-{uuid.uuid4().hex[:12]}"

523 rec = tracker.start_session(session_id, task, model="auto", provider="auto")

524 t0 = time.time()

525 try:

526 result = agent.run(task)

527 elapsed = (time.time() - t0) * 1000

528 tracker.finish_session(

529 session_id,

530 status="completed" if result.success else "error",

531 error=result.error if not result.success else "",

532 total_cost=result.total_cost_usd if hasattr(result, 'total_cost_usd') else 0.0,

533 )

534 print(f"\n{'─' * 60}")

535 if result.success:

536 print(f"Result ({(result.total_duration_ms/1000):.1f}s, "

537 f"{result.total_steps} steps, "

538 f"{result.total_tokens} tokens, "

539 f"${result.total_cost_usd:.4f}):")

540 print(f"{result.final_answer}")

541 else:

542 print(f"Error: {result.error}")

543 except Exception as e:

544 tracker.finish_session(session_id, status="error", error=str(e))

545 raise

546

547

548 def _run_marketplace(args: list[str]):

549 """Skill Marketplace CLI dispatcher."""

550

551 if not args or args[0] in ("help", "--help", "-h"):

552 print("AgentOS Skill Marketplace\n")

553 print("Subcommands:")

554 print(" search <query> 搜索技能市场")

555 print(" install <name|path|url> 安装技能（PyPI / 本地 / GitHub）")

556 print(" list 列出已安装技能")

557 print(" info <name> 查看技能详情")

558 print(" update <name> 更新技能到最新版")

559 print(" uninstall <name> 卸载技能")

560 print(" stats 市场统计")

561 print("\n兼容格式: agentos / openclaw / mcp / generic")

562 return

563

564 from agentos.marketplace import SkillRegistry, InstallResult

565

566 registry = SkillRegistry()

567 sub = args[0]

568 rest = args[1:]

569

570 if sub == "search":

571 query = " ".join(rest) if rest else ""

572 print(f"Searching marketplace for '{query or 'all'}'...\n")

573 results = registry.search(query)

574 if not results:

575 print("No skills found. Try a broader query, or publish your own with 'agentos-skill-<name>' on PyPI.")

576 return

577 print(f"{'Name':<24s} {'Version':<12s} {'Source':<10s} Description")

578 print("-" * 80)

579 for r in results:

580 desc = r.description[:60] if r.description else "-"

581 print(f"{r.name:<24s} {r.version:<12s} {r.source:<10s} {desc}")

582

583 elif sub == "install":

584 if not rest:

585 print("Usage: agentos marketplace install <name|path|url>")

586 return

587 target = " ".join(rest)

588 print(f"Installing '{target}'...")

589 result = registry.install(target)

590 if result.success and result.manifest:

591 m = result.manifest

592 print(f"\n Installed: {m.name} v{m.version} [{m.format.value}]")

593 print(f" Source: {result.install_type}")

594 if m.description:

595 print(f" Description: {m.description}")

596 if result.dep_installed:

597 print(f" Dependencies: {', '.join(result.dep_installed)}")

598 if m.tools:

599 print(f" Tools: {', '.join(t.name for t in m.tools)}")

600 else:

601 print(f" Failed: {result.error}")

602

603 elif sub == "list":

604 skills = registry.list_installed()

605 if not skills:

606 print("No skills installed. Try 'agentos marketplace install <name>'.")

607 return

608 print(f"{'Name':<24s} {'Version':<12s} {'Format':<12s} {'Source':<10s} Description")

609 print("-" * 90)

610 for m in skills:

611 desc = (m.description or "")[:50]

612 print(f"{m.name:<24s} {m.version:<12s} {m.format.value:<12s} {m.source:<10s} {desc}")

613

614 elif sub == "info":

615 if not rest:

616 print("Usage: agentos marketplace info <name>")

617 return

618 name = rest[0]

619 m = registry.get_installed(name)

620 if not m:

621 print(f"Skill '{name}' not installed. Try 'agentos marketplace search {name}'.")

622 return

623 print(f"\n {m.name} v{m.version} [{m.format.value}]")

624 print(f" {'─' * 40}")

625 print(f" 描述: {m.description or '-'}")

626 print(f" 作者: {m.author}")

627 print(f" 许可: {m.license_}")

628 print(f" 格式: {m.format.value}")

629 print(f" 来源: {m.source}")

630 print(f" 安装路径: {m.install_path or '-'}")

631 if m.entrypoint:

632 print(f" 入口: {m.entrypoint}")

633 if m.repository:

634 print(f" 仓库: {m.repository}")

635 if m.homepage:

636 print(f" 主页: {m.homepage}")

637 if m.tags:

638 print(f" 标签: {', '.join(m.tags)}")

639 if m.tools:

640 print(f" 工具 ({len(m.tools)}):")

641 for t in m.tools:

642 print(f" - {t.name}: {t.description or 'N/A'}")

643 if m.dependencies:

644 print(f" 依赖: {', '.join(m.dependencies)}")

645 if m.mcp_command:

646 print(f" MCP: {m.mcp_command} {' '.join(m.mcp_args)}")

647 print(f" MCP 类型: {m.mcp_type}")

648 if m.min_agentos_version:

649 print(f" 要求版本: >= {m.min_agentos_version}")

650 print()

651

652 elif sub == "update":

653 if not rest:

654 print("Usage: agentos marketplace update <name>")

655 return

656 name = rest[0]

657 print(f"Updating '{name}'...")

658 result = registry.update(name)

659 if result.success and result.manifest:

660 print(f" Updated: {result.manifest.name} v{result.manifest.version}")

661 else:

662 print(f" Failed: {result.error}")

663

664 elif sub == "uninstall":

665 if not rest:

666 print("Usage: agentos marketplace uninstall <name>")

667 return

668 name = rest[0]

669 if registry.uninstall(name):

670 print(f"Uninstalled: {name}")

671 else:

672 print(f"Skill '{name}' not installed.")

673

674 elif sub == "stats":

675 stats = registry.stats()

676 print(f"Marketplace Stats:")

677 print(f" Total installed: {stats['total']}")

678 print(f" Market dir: {stats['market_dir']}")

679 if stats.get("by_format"):

680 print(f" By format:")

681 for fmt, count in stats["by_format"].items():

682 print(f" {fmt}: {count}")

683

684 else:

685 print(f"Unknown marketplace command: {sub}")

686 print("Try: agentos marketplace --help")

687

688

689 def _run_enterprise(args: list[str]):

690 """Enterprise CLI dispatcher."""

691

692 if not args or args[0] in ("help", "--help", "-h"):

693 print("AgentOS Enterprise CLI\n")

694 print("Subcommands:")

695 print(" api-key create|list|revoke|stats API Key management")

696 print(" tenant create|list|stats Multi-tenant management")

697 print(" audit stats|recent|export Audit logging")

698 return

699

700 sub = args[0]

701 from agentos.enterprise import (

702 APIKeyManager, TenantManager, AuditLogger,

703 KeyCreateRequest, KeyScope, TenantTier,

704 )

705

706 if sub == "api-key":

707 _run_enterprise_api_key(args[1:], APIKeyManager())

708 elif sub == "tenant":

709 _run_enterprise_tenant(args[1:], TenantManager())

710 elif sub == "audit":

711 _run_enterprise_audit(args[1:], AuditLogger())

712 else:

713 print(f"Unknown enterprise subcommand: {sub}")

714 print("Try: agentos enterprise --help")

715

716

717 def _run_enterprise_api_key(args: list[str], mgr):

718 if not args:

719 print("Usage: agentos enterprise api-key <create|list|revoke|stats>")

720 return

721 cmd = args[0]

722 if cmd == "create":

723 name = args[1] if len(args) > 1 else "cli-key"

724 result = mgr.create_key(KeyCreateRequest(name=name, scopes=[KeyScope.AGENT_RUN, KeyScope.READ]))

725 print(f"Key created: {result.key_id}")

726 print(f"Plaintext (only shown once): {result.plaintext_key}")

727 print(f"Prefix: {result.key_prefix}")

728 print(f"Scopes: {[s.value for s in result.scopes]}")

729 elif cmd == "list":

730 keys = mgr.list_keys()

731 if not keys:

732 print("No API keys.")

733 return

734 print(f"{'ID':<28s} {'Name':<24s} {'Status':<10s} {'Usage':<8s}")

735 print("-" * 72)

736 for k in keys:

737 status = "revoked" if k.revoked else "active"

738 print(f"{k.key_id:<28s} {k.name:<24s} {status:<10s} {k.usage_count:<8d}")

739 elif cmd == "revoke":

740 if len(args) < 2:

741 print("Usage: agentos enterprise api-key revoke <key_id>")

742 return

743 ok = mgr.revoke_key(args[1])

744 print(f"{'Revoked' if ok else 'Not found or already revoked'}: {args[1]}")

745 elif cmd == "stats":

746 stats = mgr.stats()

747 print(f"Total: {stats['total']} Active: {stats['active']} Revoked: {stats['revoked']} "

748 f"Total usage: {stats['total_usage_count']}")

749 else:

750 print(f"Unknown api-key command: {cmd}")

751

752

753 def _run_enterprise_tenant(args: list[str], mgr):

754 if not args:

755 print("Usage: agentos enterprise tenant <create|list|stats>")

756 return

757 cmd = args[0]

758 if cmd == "create":

759 name = args[1] if len(args) > 1 else "default"

760 tier_str = args[2] if len(args) > 2 else "free"

761 tier = TenantTier(tier_str) if tier_str in [t.value for t in TenantTier] else TenantTier.FREE

762 tenant = mgr.create_tenant(name=name, tier=tier)

763 print(f"Tenant created: {tenant.tenant_id}")

764 print(f"Name: {tenant.name} Tier: {tenant.tier.value}")

765 elif cmd == "list":

766 tenants = mgr.list_tenants()

767 if not tenants:

768 print("No tenants.")

769 return

770 print(f"{'ID':<20s} {'Name':<20s} {'Tier':<12s} {'Status':<10s}")

771 print("-" * 64)

772 for t in tenants:

773 print(f"{t.tenant_id:<20s} {t.name:<20s} {t.tier.value:<12s} {t.status.value:<10s}")

774 elif cmd == "stats":

775 stats = mgr.stats()

776 print(f"Total tenants: {stats['total']}")

777 print(f"By tier: {stats['by_tier']}")

778 print(f"By status: {stats['by_status']}")

779 else:

780 print(f"Unknown tenant command: {cmd}")

781

782

783 def _run_enterprise_audit(args: list[str], logger):

784 if not args:

785 print("Usage: agentos enterprise audit <stats|recent|export>")

786 return

787 cmd = args[0]

788 if cmd == "stats":

789 stats = logger.stats()

790 print(f"Total events: {stats['total_events']}")

791 print(f"By category: {stats['by_category']}")

792 print(f"By severity: {stats['by_severity']}")

793 elif cmd == "recent":

794 n = int(args[1]) if len(args) > 1 else 10

795 events = logger.get_recent(n)

796 if not events:

797 print("No audit events.")

798 return

799 for e in events:

800 ts = __import__('time').strftime("%H:%M:%S", __import__('time').gmtime(e.timestamp))

801 print(f"[{ts}] {e.category.value:8s} {e.action:24s} {e.status}")

802 elif cmd == "export":

803 fmt = args[1] if len(args) > 1 else "json"

804 if fmt == "csv":

805 print(logger.export_csv()[:2000])

806 else:

807 print(logger.export_json()[:2000])

808 print("... (truncated to 2000 chars)")

809 else:

810 print(f"Unknown audit command: {cmd}")

811

812

813 def _run_demo(verbose: bool):

814 from agentos.llm import create_provider, Tool, ToolParameter

815 from agentos.agent import ToolAgent, ToolExecutor, AgentConfig

816

817 def get_weather(city: str) -> str:

818 data = {

819 "北京": "北京：晴，22°C，湿度 35%，东北风 3 级",

820 "上海": "上海：多云转阴，28°C，湿度 70%，东南风 2 级",

821 "深圳": "深圳：雷阵雨，31°C，湿度 85%",

822 }

823 return data.get(city, f"{city}: 数据暂缺")

824

825 def get_stock(symbol: str) -> str:

826 prices = {

827 "AAPL": "$220.50 (+1.2%)",

828 "TSLA": "$248.30 (-0.8%)",

829 }

830 return prices.get(symbol.upper(), f"{symbol}: 未找到")

831

832 executor = ToolExecutor()

833 executor.register(

834 Tool.from_function("get_weather", "获取城市天气", {

835 "city": ToolParameter(type="string", description="城市名"),

836 }),

837 get_weather,

838 )

839 executor.register(

840 Tool.from_function("get_stock_price", "获取股票价格", {

841 "symbol": ToolParameter(type="string", description="股票代码，如 AAPL"),

842 }),

843 get_stock,

844 )

845

846 if os.getenv("OPENAI_API_KEY"):

847 provider = create_provider("openai")

848 elif os.getenv("DEEPSEEK_API_KEY"):

849 provider = create_provider("deepseek")

850 else:

851 print("\n ⚠️ Mock 演示 — 配置 API Key 获取真实 AI 响应\n")

852 print(" 运行: agentos init\n")

853 provider = MockLLMProvider([

854 MockLLMProvider.tool_response(

855 "get_weather", {"city": "北京"}, tool_call_id="tc_w1",

856 ),

857 MockLLMProvider.text_response(

858 "北京目前天气晴，气温 22°C，湿度 35%，东北风 3 级。"

859 "适合户外活动，建议带薄外套。"

860 ),

861 ])

862

863 agent = ToolAgent(

864 provider=provider,

865 tool_executor=executor,

866 config=AgentConfig(verbose=verbose, temperature=0.0),

867 system_prompt="你是一个天气助手。用工具获取天气/股票信息后直接回答。",

868 )

869

870 result = agent.run("北京天气怎么样？")

871 print(f"\nTask: 北京天气怎么样？")

872 print(f"Steps: {result.total_steps} | Time: {(result.total_duration_ms/1000):.1f}s")

873 print(f"Tokens: {result.total_tokens} | Cost: ${result.total_cost_usd:.4f}")

874 print(f"Answer: {result.final_answer}")

875

876

877 if __name__ == "__main__":

878 main()