Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7d0ec94210 |
@@ -3,6 +3,10 @@
|
|||||||
本文件对应技能市场 **「更新日志」** Tab。
|
本文件对应技能市场 **「更新日志」** Tab。
|
||||||
发布 CI 会按当前 tag 版本提取匹配的 `##` 小节。
|
发布 CI 会按当前 tag 版本提取匹配的 `##` 小节。
|
||||||
|
|
||||||
|
## 2.0.31
|
||||||
|
|
||||||
|
- 数据管理「删除」改为技能接管(`replaces: delete`):删账号行和凭证的同时删除硬盘 Profile 目录,避免再建同名账号带回旧登录态
|
||||||
|
|
||||||
## 2.0.30
|
## 2.0.30
|
||||||
|
|
||||||
- 「打开浏览器」为本账号 Profile 附加稳定本机调试口(与科力普等采集技能约定同一端口算法),便于采集挂接已打开的原生窗口;仍使用 `--restore-last-session` 恢复登录态
|
- 「打开浏览器」为本账号 Profile 附加稳定本机调试口(与科力普等采集技能约定同一端口算法),便于采集挂接已打开的原生窗口;仍使用 `--restore-last-session` 恢复登录态
|
||||||
|
|||||||
29
SKILL.md
29
SKILL.md
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
name: 账号与凭据管理
|
name: 账号与凭据管理
|
||||||
description: "通用账号 & 凭据 & 登录流程管理器。管理网页/RPA 账号、API Key/Token;支持 9 种登录策略(账密自动、扫码、2FA、SSO 等);提供加密本地存储(Fernet);提供 rpa_helpers 库供业务 skill 同进程调用 ensure_logged_in 完成多平台自动登录。涵盖物流、LLM、内容、开发、API 五类平台。"
|
description: "通用账号 & 凭据 & 登录流程管理器。管理网页/RPA 账号、API Key/Token;支持 9 种登录策略(账密自动、扫码、2FA、SSO 等);提供加密本地存储(Fernet);提供 rpa_helpers 库供业务 skill 同进程调用 ensure_logged_in 完成多平台自动登录。涵盖物流、LLM、内容、开发、API 五类平台。"
|
||||||
version: 2.0.30
|
version: 2.0.31
|
||||||
author: 深圳匠厂科技有限公司
|
author: 深圳匠厂科技有限公司
|
||||||
metadata:
|
metadata:
|
||||||
openclaw:
|
openclaw:
|
||||||
@@ -32,19 +32,20 @@ allowed-tools:
|
|||||||
3. **登记网页/RPA 账号**:`account add-web` — 平台 + 账号名称 + 可选登录账号即可;环境/角色/登录策略跟平台默认。数据管理「新增」已绑定该能力(`replaces: create`),勿用宿主直写库新建账号。
|
3. **登记网页/RPA 账号**:`account add-web` — 平台 + 账号名称 + 可选登录账号即可;环境/角色/登录策略跟平台默认。数据管理「新增」已绑定该能力(`replaces: create`),勿用宿主直写库新建账号。
|
||||||
4. **复制账号**:`account copy <id>` — 同平台新空 Profile,名称自动加「副本」,登录账号留空。数据管理行内「复制」一点即走。
|
4. **复制账号**:`account copy <id>` — 同平台新空 Profile,名称自动加「副本」,登录账号留空。数据管理行内「复制」一点即走。
|
||||||
5. **编辑账号**:`account update --id <id>` — 只改名称、登录账号、标识类型、入口链接、状态。数据管理「编辑」已绑定(`replaces: update`),不再摊开整张表。
|
5. **编辑账号**:`account update --id <id>` — 只改名称、登录账号、标识类型、入口链接、状态。数据管理「编辑」已绑定(`replaces: update`),不再摊开整张表。
|
||||||
6. **登记凭据(API Key / Token / 加密密码)**:`account add-secret` — `none` / `env` / `windows_credential` / `local_encrypted`。
|
6. **删除账号**:`delete id <id>` — 删除账号行、关联凭证,以及硬盘上的 `profile_dir`。数据管理「删除」已绑定(`replaces: delete`),勿用宿主直写库,否则会留下旧 Profile / 登录态。
|
||||||
7. **查看/列出账号**:`account get/list` — JSON 输出;list 可按 `--login-id` / `--label` 筛选。
|
7. **登记凭据(API Key / Token / 加密密码)**:`account add-secret` — `none` / `env` / `windows_credential` / `local_encrypted`。
|
||||||
8. **用人话定位账号**:`account resolve --login-id <手机号/用户名>` 或 `--label` / `--query`(不必先查数字 id)。
|
8. **查看/列出账号**:`account get/list` — JSON 输出;list 可按 `--login-id` / `--label` 筛选。
|
||||||
9. **为 RPA 挑选账号**:`account pick-web` — 按平台/环境/角色,也可 `--login-id` / `--label`;业务首启推荐 `account ensure-web`(或 `pick-web --ensure`):无账号则自动 `add-web` 再返回。
|
9. **用人话定位账号**:`account resolve --login-id <手机号/用户名>` 或 `--label` / `--query`(不必先查数字 id)。
|
||||||
10. **获取 API Key(凭据表)**:`credential pick` — 默认隐藏 secret,`--reveal` 时才返回。
|
10. **为 RPA 挑选账号**:`account pick-web` — 按平台/环境/角色,也可 `--login-id` / `--label`;业务首启推荐 `account ensure-web`(或 `pick-web --ensure`):无账号则自动 `add-web` 再返回。
|
||||||
11. **打开浏览器**:`account open <id>`(或兼容别名 `open <id>`)— **本进程**直接拉起系统 Chrome/Edge(独立 `--user-data-dir`),最大化并跳过 Chrome 首次欢迎页,直接打开平台链接;与「打开目录」同为一点即走,不做登录判定。数据管理「打开目录 / 打开浏览器」成功不弹提示。
|
11. **获取 API Key(凭据表)**:`credential pick` — 默认隐藏 secret,`--reveal` 时才返回。
|
||||||
12. **打开目录**:`account reveal-profile <id>` — 在系统文件管理器中打开该账号 `profile_dir`。
|
12. **打开浏览器**:`account open <id>`(或兼容别名 `open <id>`)— **本进程**直接拉起系统 Chrome/Edge(独立 `--user-data-dir`),最大化并跳过 Chrome 首次欢迎页,直接打开平台链接;与「打开目录」同为一点即走,不做登录判定。数据管理「打开目录 / 打开浏览器」成功不弹提示。
|
||||||
13. **管理 lease**:`lease release/force-release/list/cleanup` — 防止 RPA 并发;数据管理「账号租约」行右键可强制释放。
|
13. **打开目录**:`account reveal-profile <id>` — 在系统文件管理器中打开该账号 `profile_dir`。
|
||||||
14. **初始化加密**:`account init-master-key` — 生成 Fernet master.key。
|
14. **管理 lease**:`lease release/force-release/list/cleanup` — 防止 RPA 并发;数据管理「账号租约」行右键可强制释放。
|
||||||
15. **加密备份与迁移**:`account export-credentials --plaintext` / `account import-credentials` — 导出明文 JSON、在新环境重新加密写入。
|
15. **初始化加密**:`account init-master-key` — 生成 Fernet master.key。
|
||||||
16. **按 lease 取明文密码**:`account get-credential <id> --reveal --lease-token <token>` — 结合 pick-web 返回的 lease 解密 `local_encrypted` 等路径。
|
16. **加密备份与迁移**:`account export-credentials --plaintext` / `account import-credentials` — 导出明文 JSON、在新环境重新加密写入。
|
||||||
17. **业务 skill 同进程登录**:`from service.rpa_helpers import ensure_logged_in` — 详见 [`references/RPA_HELPERS.md`](references/RPA_HELPERS.md)。
|
17. **按 lease 取明文密码**:`account get-credential <id> --reveal --lease-token <token>` — 结合 pick-web 返回的 lease 解密 `local_encrypted` 等路径。
|
||||||
18. **有则用、无则建(网页账号)**:`account ensure-web --platform <p>` — 登记账号 ≠ 已登录;登录仍由业务技能 / `ensure_logged_in` 处理。
|
18. **业务 skill 同进程登录**:`from service.rpa_helpers import ensure_logged_in` — 详见 [`references/RPA_HELPERS.md`](references/RPA_HELPERS.md)。
|
||||||
|
19. **有则用、无则建(网页账号)**:`account ensure-web --platform <p>` — 登记账号 ≠ 已登录;登录仍由业务技能 / `ensure_logged_in` 处理。
|
||||||
|
|
||||||
## CLI 调用
|
## CLI 调用
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# assets
|
# assets
|
||||||
|
|
||||||
- `actions.json`:匠厂宿主 Skill Action 清单(数据管理「账号信息」行内:打开目录 / 打开浏览器)。
|
- `actions.json`:匠厂宿主 Skill Action 清单(数据管理「账号信息」:新增/编辑/删除接管,行内打开目录 / 打开浏览器 / 复制)。
|
||||||
- `examples/`:CLI 成功输出形状示例(虚构路径与数据)。
|
- `examples/`:CLI 成功输出形状示例(虚构路径与数据)。
|
||||||
- `schemas/`:`get` / `list-json` / `pick-web` 单条对象的轻量 JSON Schema(`additionalProperties: true` 允许扩展字段)。
|
- `schemas/`:`get` / `list-json` / `pick-web` 单条对象的轻量 JSON Schema(`additionalProperties: true` 允许扩展字段)。
|
||||||
|
|
||||||
|
|||||||
@@ -162,6 +162,39 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": "account-delete",
|
||||||
|
"label": "删除账号",
|
||||||
|
"description": "删除账号记录、关联凭证,以及硬盘上的浏览器 Profile 目录。再建同名账号会得到空 Profile,不会带回旧登录态。",
|
||||||
|
"replaces": "delete",
|
||||||
|
"placements": [],
|
||||||
|
"executionProfile": "sync",
|
||||||
|
"interaction": "confirm",
|
||||||
|
"confirmation": {
|
||||||
|
"message": "将删除这条账号、关联凭证,以及硬盘上的浏览器 Profile 目录。登录态不可恢复。是否继续?"
|
||||||
|
},
|
||||||
|
"bind": {
|
||||||
|
"tables": ["accounts"],
|
||||||
|
"inputMapping": { "accountId": "$row.$pk" }
|
||||||
|
},
|
||||||
|
"inputSchema": {
|
||||||
|
"type": "object",
|
||||||
|
"required": ["accountId"],
|
||||||
|
"properties": {
|
||||||
|
"accountId": {
|
||||||
|
"type": "number",
|
||||||
|
"title": "账号 ID",
|
||||||
|
"readOnly": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"steps": [{ "when": "afterSuccess", "do": "reloadTable" }],
|
||||||
|
"entrypoint": {
|
||||||
|
"type": "cli",
|
||||||
|
"command": "delete",
|
||||||
|
"args": ["id", "{{accountId}}"]
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "account-copy",
|
"id": "account-copy",
|
||||||
"label": "复制",
|
"label": "复制",
|
||||||
|
|||||||
@@ -91,7 +91,7 @@
|
|||||||
| `pick-web <platform>` | `account pick-web --platform <platform>`(无 lease 参数) |
|
| `pick-web <platform>` | `account pick-web --platform <platform>`(无 lease 参数) |
|
||||||
| `list [platform\|all]` | 人类可读列表 |
|
| `list [platform\|all]` | 人类可读列表 |
|
||||||
| `add <platform> <phone>` | 映射到 `account add-web`(`login_id_type=phone`),不传 v2 扩展参数 |
|
| `add <platform> <phone>` | 映射到 `account add-web`(`login_id_type=phone`),不传 v2 扩展参数 |
|
||||||
| `delete id\|platform <...>` | 删除账号 |
|
| `delete id\|platform <...>` | 删除账号(含硬盘 `profile_dir`;数据管理「删除」走 `delete id`) |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -122,7 +122,7 @@ python main.py account update --id 4 --label "抖音 备用" --login-id 13900001
|
|||||||
|
|
||||||
- `copy`:复制平台、环境、角色、入口链接、登录策略;**新建空 Profile**;登录账号留空;名称自动「副本」/「副本 2」。不复制凭证、设备指纹、会话状态。
|
- `copy`:复制平台、环境、角色、入口链接、登录策略;**新建空 Profile**;登录账号留空;名称自动「副本」/「副本 2」。不复制凭证、设备指纹、会话状态。
|
||||||
- `update`:只改名称、登录账号、标识类型、入口链接、状态。空入口链接回退为平台默认 URL。
|
- `update`:只改名称、登录账号、标识类型、入口链接、状态。空入口链接回退为平台默认 URL。
|
||||||
- 数据管理「新增」只填平台 / 账号名称 / 登录账号;「编辑」走 `replaces: update` 短表单;行内「复制」为 `interaction: direct`。
|
- 数据管理「新增」只填平台 / 账号名称 / 登录账号;「编辑」走 `replaces: update` 短表单;「删除」走 `replaces: delete`(`delete id <id>`,含硬盘 Profile);行内「复制」为 `interaction: direct`。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
54
tests/test_account_delete.py
Normal file
54
tests/test_account_delete.py
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""delete id 会删库记录和硬盘 Profile,避免假删除残留登录态。"""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
from db.accounts_repo import find_accounts, get_account_by_id
|
||||||
|
|
||||||
|
|
||||||
|
def _add_web(**kwargs):
|
||||||
|
from service.account_service import cmd_account_add_web
|
||||||
|
|
||||||
|
defaults = dict(
|
||||||
|
platform_input="maersk",
|
||||||
|
login_id="demo@maersk.com",
|
||||||
|
login_id_type="email",
|
||||||
|
label="Maersk delete test",
|
||||||
|
tenant_id="tenant-demo",
|
||||||
|
environment="simulator",
|
||||||
|
role="booking",
|
||||||
|
provider_code=None,
|
||||||
|
url=None,
|
||||||
|
extra_json_str=None,
|
||||||
|
profile_dir_override=None,
|
||||||
|
)
|
||||||
|
defaults.update(kwargs)
|
||||||
|
cmd_account_add_web(**defaults)
|
||||||
|
|
||||||
|
|
||||||
|
def test_delete_by_id_removes_row_and_profile(clean_db, fake_env):
|
||||||
|
from service.account_service import cmd_delete_by_id
|
||||||
|
|
||||||
|
_add_web()
|
||||||
|
acc = find_accounts(platform_key="maersk")[0]
|
||||||
|
profile_dir = acc["profile_dir"]
|
||||||
|
marker = os.path.join(profile_dir, "Default", "Cookies")
|
||||||
|
os.makedirs(os.path.dirname(marker), exist_ok=True)
|
||||||
|
with open(marker, "w", encoding="utf-8") as handle:
|
||||||
|
handle.write("session")
|
||||||
|
assert os.path.isfile(marker)
|
||||||
|
|
||||||
|
cmd_delete_by_id(acc["id"])
|
||||||
|
|
||||||
|
assert get_account_by_id(acc["id"]) is None
|
||||||
|
assert find_accounts(platform_key="maersk") == []
|
||||||
|
assert not os.path.exists(profile_dir)
|
||||||
|
|
||||||
|
|
||||||
|
def test_delete_by_id_missing_account(clean_db, capsys):
|
||||||
|
from service.account_service import cmd_delete_by_id
|
||||||
|
|
||||||
|
cmd_delete_by_id(99999)
|
||||||
|
out = capsys.readouterr().out
|
||||||
|
assert "ERROR:ACCOUNT_NOT_FOUND" in out
|
||||||
@@ -72,6 +72,26 @@ class TestRowOpenActions(unittest.TestCase):
|
|||||||
self.assertIn("enum", props["login_id_type"])
|
self.assertIn("enum", props["login_id_type"])
|
||||||
self.assertIn("enum", props["status"])
|
self.assertIn("enum", props["status"])
|
||||||
|
|
||||||
|
def test_delete_takes_over_host_delete(self) -> None:
|
||||||
|
delete = _by_id(_load_actions(), "account-delete")
|
||||||
|
self.assertEqual(delete.get("replaces"), "delete")
|
||||||
|
self.assertEqual(delete.get("placements") or [], [])
|
||||||
|
self.assertEqual(delete.get("interaction"), "confirm")
|
||||||
|
self.assertEqual(delete.get("label"), "删除账号")
|
||||||
|
bind = delete.get("bind") or {}
|
||||||
|
self.assertEqual(bind.get("tables"), ["accounts"])
|
||||||
|
self.assertEqual(bind.get("inputMapping"), {"accountId": "$row.$pk"})
|
||||||
|
self.assertEqual(
|
||||||
|
(delete.get("entrypoint") or {}).get("command"),
|
||||||
|
"delete",
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
(delete.get("entrypoint") or {}).get("args"),
|
||||||
|
["id", "{{accountId}}"],
|
||||||
|
)
|
||||||
|
steps = delete.get("steps") or []
|
||||||
|
self.assertTrue(any(s.get("do") == "reloadTable" for s in steps))
|
||||||
|
|
||||||
def test_copy_is_direct_row_action(self) -> None:
|
def test_copy_is_direct_row_action(self) -> None:
|
||||||
copy = _by_id(_load_actions(), "account-copy")
|
copy = _by_id(_load_actions(), "account-copy")
|
||||||
self.assertEqual(copy.get("label"), "复制")
|
self.assertEqual(copy.get("label"), "复制")
|
||||||
|
|||||||
Reference in New Issue
Block a user