# -*- coding: utf-8 -*- """ CLI entry point: argv dispatch for account-manager v2. Subcommand tree: platform list/get/ensure account add-web|add-secret|get|get-credential|list|pick-web|mark-session|mark-error|mark-used|open init-master-key|export-credentials|import-credentials credential pick|resolve lease release|list|cleanup (legacy) add|list|get|list-json|pick-web|open|delete Machine commands output single-line JSON where appropriate. """ import sys from typing import Optional from service.account_crypto_commands import ( cmd_account_export_credentials, cmd_account_import_credentials, cmd_account_init_master_key, ) from service.account_service import ( cmd_account_add_secret, cmd_account_add_web, cmd_account_get, cmd_account_get_credential, cmd_account_list, cmd_account_mark_error, cmd_account_mark_session, cmd_account_mark_used, cmd_account_pick_web, cmd_credential_pick, cmd_credential_resolve, cmd_delete_by_id, cmd_delete_by_platform, cmd_delete_by_platform_phone, cmd_get, cmd_lease_cleanup, cmd_lease_list, cmd_lease_release, cmd_list, cmd_list_json, cmd_pick_web, cmd_platform_ensure, cmd_platform_get, cmd_platform_list, ) from service.browser_service import cmd_open from util.constants import LOG_LOGGER_NAME, SKILL_SLUG from util.logging_config import get_skill_logger, setup_skill_logging from util.platforms import _platform_list_cn_for_help, resolve_platform_key from util.runtime_paths import _maybe_print_paths_debug_cli # ========================================================================= # Help text # ========================================================================= _USAGE = """account-manager v2 — Credential & Browser Profile Manager 平台管理: platform list [--domain logistics|llm|content] platform get platform ensure --key --display-name [--domain ] [--url ] [--auth-strategy ] 账号管理: account add-web --platform

--login-id --login-id-type [--label

--credential-type --secret-storage env|windows_credential|none|local_encrypted [--secret-ref ] [--secret-stdin] [--label

] [--environment ] [--tenant-id ] [--role ] [--limit N] account pick-web --platform

[--environment ] [--tenant-id ] [--role ] [--lease] [--ttl-sec 900] [--holder ] [--purpose ] account mark-session --session-status account mark-error --code --message account mark-used account open account init-master-key [--print|--from-env|--rotate] account export-credentials --plaintext account import-credentials [--replace-all] < backup.json 凭据管理: credential pick --platform

[--type ] [--environment ] [--role ] [--reveal] credential resolve --id [--reveal] 租约管理: lease release lease list [--active] lease cleanup 浏览器: account open open # 兼容别名 兼容入口(旧版): get list-json [--limit N] pick-web list [platform|all] add delete id|platform <...> """ # ========================================================================= # Argument parsing helpers # ========================================================================= def _get_opt(argv: list[str], flag: str, default=None): """Get value of --flag from argv, returning default if not found.""" if flag in argv: idx = argv.index(flag) if idx + 1 < len(argv): return argv[idx + 1] return default def _has_flag(argv: list[str], flag: str) -> bool: return flag in argv def _get_int_opt(argv: list[str], flag: str, default: int) -> int: v = _get_opt(argv, flag) if v is not None: try: return int(v) except ValueError: return default return default def _get_all_opts(argv: list[str], flag: str) -> list[str]: """Collect all values for a repeatable flag (e.g. --platform-alias).""" vals: list[str] = [] i = 0 while i < len(argv): if argv[i] == flag and i + 1 < len(argv): vals.append(argv[i + 1]) i += 2 else: i += 1 return vals def _parse_platform_aliases(argv: list[str]) -> list[str]: aliases: list[str] = [] for raw in _get_all_opts(argv, "--platform-alias"): for part in (raw or "").split(","): s = part.strip() if s and s not in aliases: aliases.append(s) return aliases def _parse_platform_caller_meta(argv: list[str]): from service.account_service import PlatformCallerMeta display_name = _get_opt(argv, "--platform-display-name") url = _get_opt(argv, "--platform-url") domain = _get_opt(argv, "--platform-domain") auth_strategy = _get_opt(argv, "--platform-auth-strategy") aliases = _parse_platform_aliases(argv) if not any([display_name, url, domain, auth_strategy, aliases]): return None return PlatformCallerMeta( display_name=display_name, url=url, domain=domain, auth_strategy=auth_strategy, aliases=aliases, ) # ========================================================================= # Main dispatch # ========================================================================= def main() -> None: setup_skill_logging(SKILL_SLUG, LOG_LOGGER_NAME) log = get_skill_logger() if len(sys.argv) < 2: print(_USAGE) sys.exit(1) log.info("cli_start argv=%s", sys.argv) av = sys.argv cmd = av[1] _maybe_print_paths_debug_cli() # Determine if first arg is a platform key (for legacy shorthand) plat_from_first = resolve_platform_key(cmd) # ---- platform subcommand ---- if cmd == "platform": if len(av) < 3: print("ERROR:CLI_BAD_ARGS platform 需要子命令:list | get | ensure") sys.exit(1) sub = av[2] if sub == "list": domain = _get_opt(av, "--domain") cmd_platform_list(domain) elif sub == "get": if len(av) < 4: print("ERROR:CLI_BAD_ARGS platform get 需要 。") sys.exit(1) cmd_platform_get(av[3]) elif sub == "ensure": import argparse as _ap p = _ap.ArgumentParser(prog="platform ensure") p.add_argument("--key", required=True) p.add_argument("--display-name", default="") p.add_argument("--domain", default="generic") p.add_argument("--url", default="") p.add_argument("--auth-strategy", default=None) p.add_argument("--aliases", default=None, help="JSON 数组字符串,如 '[\"1688\",\"阿里巴巴1688\"]'") p.add_argument("--capabilities", default=None, help='JSON 对象,如 \'{"web":true,"api_key":false,"rpa":true}\'') args = p.parse_args(av[3:]) sys.exit(cmd_platform_ensure( key=args.key, display_name=args.display_name or args.key, domain=args.domain, url=args.url, auth_strategy=args.auth_strategy, aliases=args.aliases, capabilities=args.capabilities, )) else: print("ERROR:CLI_BAD_ARGS platform 子命令必须是 list、get 或 ensure。") sys.exit(1) # ---- account subcommand ---- elif cmd == "account": if len(av) < 3: print("ERROR:CLI_BAD_ARGS account 需要子命令(add-web / get / …)。") print(_USAGE) sys.exit(1) sub = av[2] if sub == "add-web": _cmd_add_web(av) elif sub == "add-secret": _cmd_add_secret(av) elif sub == "get-credential": if len(av) < 4: print("ERROR:CLI_BAD_ARGS account get-credential 需要 。") sys.exit(1) _cmd_get_credential(av) elif sub == "get": if len(av) < 4: print("ERROR:CLI_BAD_ARGS account get 需要 。") sys.exit(1) aid = av[3] with_creds = _has_flag(av, "--with-credentials") cmd_account_get(aid, include_credentials=with_creds) elif sub == "list": _cmd_account_list(av) elif sub == "pick-web": _cmd_pick_web_new(av) elif sub == "mark-session": if len(av) < 4: print("ERROR:CLI_BAD_ARGS account mark-session 需要 。") sys.exit(1) aid = av[3] ss = _get_opt(av, "--session-status", "unknown") cmd_account_mark_session(aid, ss) elif sub == "mark-error": if len(av) < 4: print("ERROR:CLI_BAD_ARGS account mark-error 需要 。") sys.exit(1) aid = av[3] ec = _get_opt(av, "--code", "") em = _get_opt(av, "--message", "") cmd_account_mark_error(aid, ec, em) elif sub == "mark-used": if len(av) < 4: print("ERROR:CLI_BAD_ARGS account mark-used 需要 。") sys.exit(1) aid = av[3] cmd_account_mark_used(aid) elif sub == "open": if len(av) < 4: print("ERROR:CLI_BAD_ARGS account open 需要 。") sys.exit(1) cmd_open(av[3]) elif sub == "init-master-key": cmd_account_init_master_key(av) elif sub == "export-credentials": cmd_account_export_credentials(av) elif sub == "import-credentials": cmd_account_import_credentials(av) else: print("ERROR:CLI_BAD_ARGS 未知的 account 子命令。") print(_USAGE) sys.exit(1) # ---- credential subcommand ---- elif cmd == "credential": if len(av) < 3: print("ERROR:CLI_BAD_ARGS credential 需要子命令:pick | resolve") sys.exit(1) sub = av[2] if sub == "pick": _cmd_cred_pick(av) elif sub == "resolve": _cmd_cred_resolve(av) else: print("ERROR:CLI_BAD_ARGS credential 子命令必须是 pick 或 resolve。") sys.exit(1) # ---- lease subcommand ---- elif cmd == "lease": if len(av) < 3: print("ERROR:CLI_BAD_ARGS lease 需要子命令:release | list | cleanup") sys.exit(1) sub = av[2] if sub == "release": if len(av) < 4: print("ERROR:CLI_BAD_ARGS lease release 需要 。") sys.exit(1) cmd_lease_release(av[3]) elif sub == "list": active = _has_flag(av, "--active") cmd_lease_list(active) elif sub == "cleanup": cmd_lease_cleanup() else: print("ERROR:CLI_BAD_ARGS lease 子命令必须是 release、list 或 cleanup。") sys.exit(1) # ---- Legacy aliases ---- elif cmd == "get": if len(sys.argv) < 3: print("ERROR:CLI_GET_MISSING_ID") sys.exit(1) cmd_get(sys.argv[2]) elif cmd == "list-json": if len(sys.argv) < 3: print("ERROR:CLI_LIST_JSON_MISSING_PLATFORM") sys.exit(1) av = sys.argv[2:] platform_arg = av[0] lim = 200 if "--limit" in av: i = av.index("--limit") if i + 1 < len(av): try: lim = int(av[i + 1]) except ValueError: pass cmd_list_json(platform_arg, limit=lim) elif cmd == "pick-web": if len(sys.argv) < 3: print("ERROR:CLI_PICK_WEB_MISSING_ARGS") sys.exit(1) cmd_pick_web(sys.argv[2]) elif cmd == "open": if len(sys.argv) < 3: print("ERROR:CLI_OPEN_MISSING_ID") sys.exit(1) cmd_open(sys.argv[2]) elif cmd == "list": platform_arg = sys.argv[2] if len(sys.argv) >= 3 else "all" cmd_list(platform_arg) elif cmd == "add": if len(sys.argv) < 4: print("ERROR:CLI_ADD_MISSING_ARGS") sys.exit(1) # Legacy add maps to add-web for backward compat cmd_account_add_web( platform_input=sys.argv[2], login_id=sys.argv[3], login_id_type="phone", label=None, tenant_id=None, environment="production", role="default", provider_code=None, url=None, extra_json_str=None, profile_dir_override=None, ) elif cmd == "delete": if len(sys.argv) < 4: print("ERROR:CLI_DELETE_MISSING_ARGS") sys.exit(1) mode = (sys.argv[2] or "").strip().lower() if mode == "id": cmd_delete_by_id(sys.argv[3]) elif mode == "platform": if len(sys.argv) >= 5: cmd_delete_by_platform_phone(sys.argv[3], sys.argv[4]) else: cmd_delete_by_platform(sys.argv[3]) else: print(f"ERROR:CLI_DELETE_BAD_MODE 不支持的删除方式「{sys.argv[2]}」。") sys.exit(1) # ---- Platform shorthand: "python main.py " ---- elif plat_from_first: cmd_list(sys.argv[1]) # ---- Unknown ---- else: print(f"ERROR:CLI_UNKNOWN_OR_BAD_ARGS 子命令「{cmd}」无法识别。") print() print(_USAGE) sys.exit(1) # ========================================================================= # Sub-parsers for complex commands # ========================================================================= def _cmd_add_web(argv: list[str]) -> None: plat = _get_opt(argv, "--platform", "") login_id = _get_opt(argv, "--login-id") login_id_type = _get_opt(argv, "--login-id-type", "unknown") label = _get_opt(argv, "--label") tenant_id = _get_opt(argv, "--tenant-id") environment = _get_opt(argv, "--environment", "production") role = _get_opt(argv, "--role", "default") provider_code = _get_opt(argv, "--provider-code") url = _get_opt(argv, "--url") extra_json_str = _get_opt(argv, "--extra-json") profile_dir_override = _get_opt(argv, "--profile-dir") auth_strategy = _get_opt(argv, "--auth-strategy") session_persistent_raw = _get_opt(argv, "--session-persistent") device_fingerprint = _get_opt(argv, "--device-fingerprint") session_persistent: Optional[int] = None if session_persistent_raw is not None: try: session_persistent = int(session_persistent_raw) except ValueError: print("ERROR:CLI_BAD_ARGS --session-persistent 必须是整数(0 或 1)。") return if not plat: print("ERROR:CLI_BAD_ARGS account add-web 需要 --platform 。") return platform_meta = _parse_platform_caller_meta(argv) cmd_account_add_web( platform_input=plat, login_id=login_id, login_id_type=login_id_type, label=label, tenant_id=tenant_id, environment=environment, role=role, provider_code=provider_code, url=url, extra_json_str=extra_json_str, profile_dir_override=profile_dir_override, auth_strategy=auth_strategy, session_persistent=session_persistent, device_fingerprint=device_fingerprint, platform_meta=platform_meta, ) def _cmd_get_credential(argv: list[str]) -> None: aid_raw = argv[3] try: aid = int(aid_raw) except ValueError: print(f"ERROR:CLI_BAD_ARGS account_id 必须是整数:{aid_raw}") return reveal = _has_flag(argv, "--reveal") lease_token = _get_opt(argv, "--lease-token") cmd_account_get_credential(aid, reveal=reveal, lease_token=lease_token) def _cmd_add_secret(argv: list[str]) -> None: plat = _get_opt(argv, "--platform", "") ctype = _get_opt(argv, "--credential-type", "api_key") label = _get_opt(argv, "--label") storage = _get_opt(argv, "--secret-storage", "env") stdin_mode = _has_flag(argv, "--secret-stdin") sref = _get_opt(argv, "--secret-ref") aid = _get_int_opt(argv, "--account-id", 0) or None env = _get_opt(argv, "--environment", "production") role = _get_opt(argv, "--role", "api") extra_json_str = _get_opt(argv, "--extra-json") if not plat: print("ERROR:CLI_BAD_ARGS account add-secret 需要 --platform 。") return cmd_account_add_secret( platform_input=plat, credential_type=ctype, label=label, secret_storage=storage, secret_stdin=stdin_mode, secret_ref=sref, account_id=aid, environment=env, role=role, extra_json_str=extra_json_str, ) def _cmd_account_list(argv: list[str]) -> None: plat = _get_opt(argv, "--platform") env = _get_opt(argv, "--environment") tenant = _get_opt(argv, "--tenant-id") role = _get_opt(argv, "--role") limit = _get_int_opt(argv, "--limit", 200) cmd_account_list( platform_input=plat, environment=env, tenant_id=tenant, role=role, limit=limit, ) def _cmd_pick_web_new(argv: list[str]) -> None: plat = _get_opt(argv, "--platform", "") env = _get_opt(argv, "--environment") tenant = _get_opt(argv, "--tenant-id") role = _get_opt(argv, "--role") do_lease = _has_flag(argv, "--lease") ttl = _get_int_opt(argv, "--ttl-sec", 900) holder = _get_opt(argv, "--holder") purpose = _get_opt(argv, "--purpose") if not plat: print("ERROR:CLI_BAD_ARGS account pick-web 需要 --platform 。") return platform_meta = _parse_platform_caller_meta(argv) cmd_account_pick_web( platform_input=plat, environment=env, tenant_id=tenant, role=role, do_lease=do_lease, ttl_sec=ttl, holder=holder, purpose=purpose, platform_meta=platform_meta, ) def _cmd_cred_pick(argv: list[str]) -> None: plat = _get_opt(argv, "--platform", "") ctype = _get_opt(argv, "--type") env = _get_opt(argv, "--environment") role = _get_opt(argv, "--role") reveal = _has_flag(argv, "--reveal") if not plat: print("ERROR:CLI_BAD_ARGS credential pick 需要 --platform 。") return cmd_credential_pick( platform_input=plat, credential_type=ctype, environment=env, role=role, reveal=reveal, ) def _cmd_cred_resolve(argv: list[str]) -> None: cid = _get_int_opt(argv, "--id", 0) reveal = _has_flag(argv, "--reveal") if cid <= 0: print("ERROR:CLI_BAD_ARGS credential resolve 需要 --id 。") return cmd_credential_resolve(cid, reveal=reveal) if __name__ == "__main__": main()