flower-trellis 0.6.10-beta.4 → 0.6.10-beta.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/enhancements/0.6/scripts/task_lifecycle.py +59 -8
- package/enhancements/MANIFEST.json +2 -2
- package/enhancements/common/.common/.claude/skills/aliyun-ops/SKILL.md +4 -3
- package/enhancements/common/.common/.claude/skills/aliyun-ops/assets/env.example +4 -0
- package/enhancements/common/.common/.claude/skills/aliyun-ops/references/ack.md +35 -5
- package/enhancements/common/.common/.claude/skills/aliyun-ops/scripts/ack.py +529 -74
- package/enhancements/common/.common/.claude/skills/aliyun-ops/scripts/dms.py +10 -8
- package/enhancements/common/.common/.codex/skills/aliyun-ops/SKILL.md +4 -3
- package/enhancements/common/.common/.codex/skills/aliyun-ops/assets/env.example +4 -0
- package/enhancements/common/.common/.codex/skills/aliyun-ops/references/ack.md +35 -5
- package/enhancements/common/.common/.codex/skills/aliyun-ops/scripts/ack.py +529 -74
- package/enhancements/common/.common/.codex/skills/aliyun-ops/scripts/dms.py +10 -8
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -172,7 +172,7 @@ flower-trellis plugin
|
|
|
172
172
|
|
|
173
173
|
交互管理器采用 `发现 / 已安装 / 来源 / 问题` 四个页签。Trellis 项目的 `发现` 页会展示 `flower/skill-garden` 内置入口,按 Enter 直接管理工作流强化与可选通用技能;原 `flower-trellis skill` 命令继续保留为高级兼容入口。`发现` 同时合并全部已启用来源的 Plugin,并保留来源标签和即时搜索;未登录 GitLab 来源会直接进入 Device Flow,GitHub 公共来源无需登录。`来源` 页的“新增来源”可选择 GitHub 公共仓库或 GitLab Marketplace;GitHub 会先在临时缓存中下载固定快照、检测格式、展示可导入与忽略组件,确认后才保存。ref 留空时使用仓库默认分支;出现多个格式入口时会要求选择,公开 GitHub 跨仓 Marketplace 条目和 `plugins/*` 多 Plugin 仓库也可识别。
|
|
174
174
|
|
|
175
|
-
可选通用技能包含 `aliyun-ops`,统一提供阿里云 DMS、SLS、MSE 与 ACK 查询和排障能力;ACK 支持通过短期 KubeConfig 和 Workbench 查询私网 Kubernetes
|
|
175
|
+
可选通用技能包含 `aliyun-ops`,统一提供阿里云 DMS、SLS、MSE 与 ACK 查询和排障能力;ACK 支持通过短期 KubeConfig 和 Workbench 查询私网 Kubernetes 资源,并提供 Deployment 切镜像与改环境变量的受控命令(默认预览、显式 `--yes` 才执行,可通过 ENV 白名单限定命名空间与镜像仓库),DMS 写操作通过数据变更工单进入审批流。通用技能需手动选择启用,不会复制用户私有 AK/SK 或 Workbench 配置。Codex 通用技能安装到 `.agents/skills`,Claude 安装到 `.claude/skills`;已有 `.codex` 或 `.agents` 时使用共享目标,已有 `.claude` 时使用 Claude 目标,三个目录都不存在时默认同时安装两份。空目录也可通过 Skill Garden 入口管理通用技能,无需初始化 Trellis。
|
|
176
176
|
|
|
177
177
|
安装、更新和卸载都会先展示 dry-run、依赖、capability 和目标文件变化,确认后才写入项目。Plugin 作者使用的 `plugin init`、`plugin validate` 继续保留在高级命令中,不占用普通用户的管理器首页。
|
|
178
178
|
|
|
@@ -603,6 +603,17 @@ def _tracked_files(repo_root: Path, relative: str) -> list[str]:
|
|
|
603
603
|
return sorted(path for path in output.split("\0") if path)
|
|
604
604
|
|
|
605
605
|
|
|
606
|
+
def _untracked_files(repo_root: Path, relative: str) -> list[str]:
|
|
607
|
+
"""列出路径下可由 Git 纳管的未跟踪文件。"""
|
|
608
|
+
code, output, error = run_git(
|
|
609
|
+
["ls-files", "-z", "--others", "--exclude-standard", "--", relative],
|
|
610
|
+
cwd=repo_root,
|
|
611
|
+
)
|
|
612
|
+
if code != 0:
|
|
613
|
+
raise TaskLifecycleError("git-ls-files-failed", error.strip() or "无法读取未跟踪文件")
|
|
614
|
+
return sorted(path for path in output.split("\0") if path)
|
|
615
|
+
|
|
616
|
+
|
|
606
617
|
def _run_git_with_index(
|
|
607
618
|
args: list[str],
|
|
608
619
|
repo_root: Path,
|
|
@@ -1245,18 +1256,36 @@ def _write_batch_and_commit(
|
|
|
1245
1256
|
message: str,
|
|
1246
1257
|
baseline: dict[str, str],
|
|
1247
1258
|
transaction: dict[str, Any],
|
|
1259
|
+
*,
|
|
1260
|
+
commit_paths: Optional[list[str]] = None,
|
|
1261
|
+
expected_files: Optional[set[str]] = None,
|
|
1248
1262
|
) -> str:
|
|
1249
|
-
"""原子写入一批 JSON,并以可恢复的精确 pathspec 创建本地提交。
|
|
1263
|
+
"""原子写入一批 JSON,并以可恢复的精确 pathspec 创建本地提交。
|
|
1264
|
+
|
|
1265
|
+
Args:
|
|
1266
|
+
repo_root: Git worktree 根目录。
|
|
1267
|
+
writes: 需要原子写入的 JSON 路径与数据。
|
|
1268
|
+
message: maintenance commit 消息。
|
|
1269
|
+
baseline: 事务开始时固定的完整分支引用与 HEAD。
|
|
1270
|
+
transaction: 操作类型与可恢复结果。
|
|
1271
|
+
commit_paths: 可选的精确提交 pathspec;缺省仅提交写入的 JSON。
|
|
1272
|
+
expected_files: 可选的预期提交文件全集;缺省等于写入的 JSON。
|
|
1273
|
+
|
|
1274
|
+
Returns:
|
|
1275
|
+
新创建并绑定到固定分支的提交哈希。
|
|
1276
|
+
"""
|
|
1250
1277
|
originals = [(path, path.read_bytes()) for path, _ in writes]
|
|
1251
1278
|
relative_paths = [_task_relative(repo_root, path) for path, _ in writes]
|
|
1279
|
+
exact_paths = relative_paths if commit_paths is None else commit_paths
|
|
1280
|
+
exact_files = set(relative_paths) if expected_files is None else expected_files
|
|
1252
1281
|
try:
|
|
1253
1282
|
for path, data in writes:
|
|
1254
1283
|
if not write_json(path, data):
|
|
1255
1284
|
raise TaskLifecycleError("write-failed", f"无法写入:{_task_relative(repo_root, path)}")
|
|
1256
1285
|
return _commit_exact_paths(
|
|
1257
1286
|
repo_root,
|
|
1258
|
-
|
|
1259
|
-
|
|
1287
|
+
exact_paths,
|
|
1288
|
+
exact_files,
|
|
1260
1289
|
message,
|
|
1261
1290
|
baseline,
|
|
1262
1291
|
transaction,
|
|
@@ -1337,18 +1366,31 @@ def reconcile_legacy_tasks(repo_root: Path, migration_time: Optional[str] = None
|
|
|
1337
1366
|
return result
|
|
1338
1367
|
timestamp = migration_time or utc_now()
|
|
1339
1368
|
writes: list[tuple[Path, dict[str, Any]]] = []
|
|
1369
|
+
commit_paths: list[str] = []
|
|
1370
|
+
expected_files: set[str] = set()
|
|
1340
1371
|
for task_dir, data in legacy_dirs:
|
|
1341
1372
|
task_json = task_dir / FILE_TASK_JSON
|
|
1342
1373
|
task_ref = _task_relative(repo_root, task_dir)
|
|
1374
|
+
task_json_ref = f"{task_ref}/{FILE_TASK_JSON}"
|
|
1343
1375
|
path_status = _git_path_status(repo_root, [task_ref])
|
|
1376
|
+
status_records = _status_records(path_status)
|
|
1344
1377
|
dirty_paths = {
|
|
1345
1378
|
path
|
|
1346
|
-
for _, paths in
|
|
1379
|
+
for _, paths in status_records
|
|
1347
1380
|
for path in paths
|
|
1348
1381
|
}
|
|
1382
|
+
tracked_files = _tracked_files(repo_root, task_ref)
|
|
1383
|
+
untracked_files = set(_untracked_files(repo_root, task_ref)) if not tracked_files else set()
|
|
1384
|
+
entirely_untracked = bool(
|
|
1385
|
+
status_records
|
|
1386
|
+
and not tracked_files
|
|
1387
|
+
and task_json_ref in untracked_files
|
|
1388
|
+
and dirty_paths == untracked_files
|
|
1389
|
+
and all(git_status == "??" for git_status, _ in status_records)
|
|
1390
|
+
)
|
|
1349
1391
|
runner_owned = (
|
|
1350
1392
|
task_ref in recorded
|
|
1351
|
-
and dirty_paths == {
|
|
1393
|
+
and dirty_paths == {task_json_ref}
|
|
1352
1394
|
and (
|
|
1353
1395
|
recorded_digests.get(task_ref) == _task_json_sha256(task_json)
|
|
1354
1396
|
or _legacy_runner_bookkeeping_matches(
|
|
@@ -1359,10 +1401,10 @@ def reconcile_legacy_tasks(repo_root: Path, migration_time: Optional[str] = None
|
|
|
1359
1401
|
)
|
|
1360
1402
|
)
|
|
1361
1403
|
)
|
|
1362
|
-
if path_status and not runner_owned:
|
|
1404
|
+
if path_status and not runner_owned and not entirely_untracked:
|
|
1363
1405
|
result["deferred"].append({"task": task_ref, "reason": "candidate-dirty"})
|
|
1364
1406
|
continue
|
|
1365
|
-
if not
|
|
1407
|
+
if not tracked_files and not entirely_untracked:
|
|
1366
1408
|
result["deferred"].append({"task": task_ref, "reason": "candidate-untracked"})
|
|
1367
1409
|
continue
|
|
1368
1410
|
next_data = dict(data)
|
|
@@ -1375,7 +1417,7 @@ def reconcile_legacy_tasks(repo_root: Path, migration_time: Optional[str] = None
|
|
|
1375
1417
|
task_dir,
|
|
1376
1418
|
repo_root,
|
|
1377
1419
|
next_data,
|
|
1378
|
-
delivery_verified=not path_status or runner_owned,
|
|
1420
|
+
delivery_verified=not path_status or runner_owned or entirely_untracked,
|
|
1379
1421
|
closed_at=timestamp,
|
|
1380
1422
|
)
|
|
1381
1423
|
if evaluated["status"] == "error":
|
|
@@ -1389,6 +1431,13 @@ def reconcile_legacy_tasks(repo_root: Path, migration_time: Optional[str] = None
|
|
|
1389
1431
|
result["deferred"].append({"task": task_ref, "reason": "unknown-task-status"})
|
|
1390
1432
|
continue
|
|
1391
1433
|
writes.append((task_json, next_data))
|
|
1434
|
+
if entirely_untracked:
|
|
1435
|
+
# 新任务尚无 HEAD 基线,只能把可纳管的完整目录作为一个原子任务记录提交。
|
|
1436
|
+
commit_paths.append(task_ref)
|
|
1437
|
+
expected_files.update(untracked_files)
|
|
1438
|
+
else:
|
|
1439
|
+
commit_paths.append(task_json_ref)
|
|
1440
|
+
expected_files.add(task_json_ref)
|
|
1392
1441
|
result["migrated"].append({"task": task_ref, "closeout": migration_status})
|
|
1393
1442
|
if writes:
|
|
1394
1443
|
result["commit"] = _write_batch_and_commit(
|
|
@@ -1397,6 +1446,8 @@ def reconcile_legacy_tasks(repo_root: Path, migration_time: Optional[str] = None
|
|
|
1397
1446
|
"chore(task): reconcile closeout metadata",
|
|
1398
1447
|
baseline,
|
|
1399
1448
|
{"operation": "reconciliation", "result": result},
|
|
1449
|
+
commit_paths=commit_paths,
|
|
1450
|
+
expected_files=expected_files,
|
|
1400
1451
|
)
|
|
1401
1452
|
return result
|
|
1402
1453
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
|
-
"syncedAt": "2026-09-
|
|
2
|
+
"syncedAt": "2026-09-23T10:30:48.306Z",
|
|
3
3
|
"syncedFrom": "vendor/skill-garden",
|
|
4
|
-
"sourceCommit": "
|
|
4
|
+
"sourceCommit": "f2feb8b818f72b124466a186987690f69665e8d1",
|
|
5
5
|
"common": {
|
|
6
6
|
"codexSkills": [
|
|
7
7
|
"aliyun-ops",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: aliyun-ops
|
|
3
|
-
description: "统一查询阿里云 DMS、SLS、MSE/Nacos 与 ACK 运维数据。当需要查询线上数据库、执行只读 SQL、预览或提交 DMS 数据变更工单、拉取 SLS 日志或指标、核对 Nacos 配置、查看 ACK
|
|
3
|
+
description: "统一查询阿里云 DMS、SLS、MSE/Nacos 与 ACK 运维数据。当需要查询线上数据库、执行只读 SQL、预览或提交 DMS 数据变更工单、拉取 SLS 日志或指标、核对 Nacos 配置、查看 ACK 集群和节点、通过 Workbench 查询私网 Kubernetes 资源、ConfigMap 与 Secret 键名,或在用户确认后受控切换 Deployment 镜像与环境变量、等待发布结果时使用。"
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# 阿里云运维查询
|
|
@@ -14,7 +14,7 @@ description: "统一查询阿里云 DMS、SLS、MSE/Nacos 与 ACK 运维数据
|
|
|
14
14
|
| 线上数据库查询、DMS 实例/库、数据变更工单 | `scripts/dms.py` | `references/dms.md` |
|
|
15
15
|
| SLS 日志、metricstore 指标、LOG V1 签名 | `scripts/sls_get_logs.py` | `references/sls.md` |
|
|
16
16
|
| MSE 集群、Nacos 配置与配置历史 | `scripts/mse.py` | `references/mse.md` |
|
|
17
|
-
| ACK 集群、节点、KubeConfig、Kubernetes
|
|
17
|
+
| ACK 集群、节点、KubeConfig、Kubernetes 资源只读查询;Deployment 切镜像、改环境变量、等待发布 | `scripts/ack.py` | `references/ack.md` |
|
|
18
18
|
|
|
19
19
|
DMS 与 MSE 复用 `scripts/aliyun_rpc_v1.py`。SLS 使用独立的 LOG V1 签名,ACK 使用 `scripts/ack_roa_v3.py` 的 ACS3-HMAC-SHA256 签名,禁止混用三种协议。新增其它产品时,业务参数、响应解析和安全边界继续放在产品脚本与 reference 中。
|
|
20
20
|
|
|
@@ -34,7 +34,8 @@ DMS 与 MSE 复用 `scripts/aliyun_rpc_v1.py`。SLS 使用独立的 LOG V1 签
|
|
|
34
34
|
- 默认读取顺序为进程环境 → `~/.config/aliyun-ops/env` → 产品旧路径;后读文件只补空值,不覆盖已有变量。
|
|
35
35
|
- DMS 写操作只能走工单,且真实提交必须显式带 `--yes`。MSE 本期只提供读命令。
|
|
36
36
|
- MSE 当前配置与历史配置无 `--grep` 时只输出摘要;不得为了方便绕过这一边界输出完整配置。
|
|
37
|
-
- ACK
|
|
37
|
+
- ACK 查询命令只读。私网 APIServer 使用短期内网 KubeConfig 加 Workbench 跳板;不提供任意远程 shell 或 `apply/delete/patch/scale`。
|
|
38
|
+
- ACK 写入只能通过 `set-image` / `set-env`:默认只预览,用户确认后显式带 `--yes` 才执行;`ALIYUN_ACK_WRITE_NAMESPACES`、`ALIYUN_ACK_IMAGE_PREFIXES` 配置时强制白名单。禁止绕过这两个命令,直接用 `workbench exec`、手工 KubeConfig 或临时脚本执行任何写操作;需求超出这两个命令时,先向用户说明,不要自行变通。
|
|
38
39
|
- ACK 查询先列资源摘要,再按用户明确目标读取单个对象。Secret 只输出键名和脱敏值,KubeConfig 默认不回显正文。
|
|
39
40
|
|
|
40
41
|
配置模板位于 `assets/env.example`。初始化新文件时由用户主动执行:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
# 阿里云 ACK
|
|
1
|
+
# 阿里云 ACK 查询与受控变更
|
|
2
2
|
|
|
3
|
-
用于查询 ACK 控制面信息,以及在本机无法访问私网 APIServer 时,通过 ACK Worker 和 Workbench CLI 查询 Kubernetes
|
|
3
|
+
用于查询 ACK 控制面信息,以及在本机无法访问私网 APIServer 时,通过 ACK Worker 和 Workbench CLI 查询 Kubernetes 对象。查询入口全部只读;变更只提供 Deployment 切镜像与改环境变量两个受控命令,默认预览、显式 `--yes` 才执行。
|
|
4
4
|
|
|
5
5
|
## 查询顺序
|
|
6
6
|
|
|
@@ -57,6 +57,35 @@ Workbench 路径固定签发内网临时 KubeConfig,默认有效期 30 分钟
|
|
|
57
57
|
|
|
58
58
|
Secret 的 `data` 和 `stringData` 值始终输出 `<REDACTED>`,只保留键名。ConfigMap 可在用户明确指定名称后输出正文;列表摘要只显示名称和键。
|
|
59
59
|
|
|
60
|
+
## 受控写命令
|
|
61
|
+
|
|
62
|
+
只在用户明确要求变更时使用。先不带 `--yes` 运行预览,把集群、命名空间、Deployment、容器、当前值 → 目标值和白名单状态展示给用户,获得确认后原样加 `--yes` 重跑。
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
# 切换或回退镜像(回退 = 用旧 tag 再执行一次)
|
|
66
|
+
python3 scripts/ack.py set-image --cluster <cluster-id> --namespace <namespace> \
|
|
67
|
+
--deployment <deployment> [--container <container>] --image <registry/repo:tag>
|
|
68
|
+
|
|
69
|
+
# 设置或删除指定环境变量键(均可重复)
|
|
70
|
+
python3 scripts/ack.py set-env --cluster <cluster-id> --namespace <namespace> \
|
|
71
|
+
--deployment <deployment> [--container <container>] --set KEY=VALUE --unset OLD_KEY
|
|
72
|
+
|
|
73
|
+
# 确认后执行并在同一会话等待发布完成
|
|
74
|
+
python3 scripts/ack.py set-image ... --yes --wait --timeout 300
|
|
75
|
+
|
|
76
|
+
# 只读等待或查看发布状态
|
|
77
|
+
python3 scripts/ack.py rollout-status --cluster <cluster-id> --namespace <namespace> \
|
|
78
|
+
--deployment <deployment> --timeout 300
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
- 写命令固定经 Workbench 执行,复用临时内网 KubeConfig 与两端清理流程,无需 `--via-workbench`。
|
|
82
|
+
- Deployment 只有一个容器时可省略 `--container`;多个容器必须显式指定。
|
|
83
|
+
- 镜像必须带 `:tag` 或 `@sha256:` digest;当前值与目标值一致时不执行写入。
|
|
84
|
+
- `set-env` 只修改指定键,拒绝修改通过 `valueFrom` 引用 Secret/ConfigMap 的键;不支持批量导入。
|
|
85
|
+
- `--timeout` 为 10~570 秒。写入成功但等待或清理失败时,错误会注明"写入已生效",不要重复执行写命令,先用 `rollout-status` 或 `get` 核对。
|
|
86
|
+
- 在私有 ENV 中配置 `ALIYUN_ACK_WRITE_NAMESPACES`、`ALIYUN_ACK_IMAGE_PREFIXES`(逗号分隔)后,未命中的命名空间或镜像在任何云调用前失败;留空表示不限制。
|
|
87
|
+
- 不提供重启、扩缩容、删除、任意 `patch` 或远程 shell。需求超出以上命令时向用户说明,禁止改用 `workbench exec` 或临时脚本自行执行写操作。
|
|
88
|
+
|
|
60
89
|
## Workbench 前置条件
|
|
61
90
|
|
|
62
91
|
- 本机已安装官方 `workbench` CLI,`workbench version` 可执行。
|
|
@@ -78,14 +107,14 @@ Secret 的 `data` 和 `stringData` 值始终输出 `<REDACTED>`,只保留键
|
|
|
78
107
|
| 列集群 | `cs:GetClusters` |
|
|
79
108
|
| 集群详情 | `cs:DescribeClusterDetail` |
|
|
80
109
|
| 关联云资源 | `cs:DescribeClusterResources` |
|
|
81
|
-
|
|
|
82
|
-
| KubeConfig
|
|
110
|
+
| 节点列表与 Workbench 自动选节点 | `cs:DescribeClusterNodes` |
|
|
111
|
+
| KubeConfig、`get`、受控写命令与 `rollout-status` | `cs:DescribeClusterUserKubeconfig` |
|
|
83
112
|
|
|
84
113
|
临时 KubeConfig 有效期还可通过 `cs:KubeConfigDurationMinutes` 条件收敛。
|
|
85
114
|
|
|
86
115
|
### ACK 集群 RBAC
|
|
87
116
|
|
|
88
|
-
KubeConfig 只代表当前 RAM 身份。该身份还必须在目标集群与命名空间内拥有对应资源的 `get`、列表场景的 `list`
|
|
117
|
+
KubeConfig 只代表当前 RAM 身份。该身份还必须在目标集群与命名空间内拥有对应资源的 `get`、列表场景的 `list` 权限;受控写命令还需要 `deployments` 的 `patch` 权限,`--wait` / `rollout-status` 需要 `deployments`、`replicasets` 的 `get`/`list`/`watch`。OpenAPI 成功不代表 Kubernetes RBAC 已授权。
|
|
89
118
|
|
|
90
119
|
### Workbench / ECS RAM
|
|
91
120
|
|
|
@@ -104,6 +133,7 @@ KubeConfig 只代表当前 RAM 身份。该身份还必须在目标集群与命
|
|
|
104
133
|
- `无法连通 APIServer`:先看 `detail`。只有内网端点时改用 `--via-workbench`,不建议为了查询临时开放生产公网端点。
|
|
105
134
|
- `Forbidden`:检查 ACK 集群 RBAC;这与 `cs:*` RAM 权限无关。
|
|
106
135
|
- `profile not found`:运行 `workbench config list` 核对 profile,并通过 `--workbench-profile` 指定。
|
|
136
|
+
- Workbench 上传偶发失败:脚本会自动重试一次;仍失败时错误只保留去掉进度动画后的真实原因。
|
|
107
137
|
- Workbench 登录或上传 403:逐项核对 Workbench/ECS RAM Action、实例范围和服务关联角色。
|
|
108
138
|
- 无可自动选择 Worker:运行 `nodes` 查看状态,确认存在 Running/Ready 的阿里云 Worker,或显式传 `--instance-id`。
|
|
109
139
|
- 远端没有 `kubectl`:更换 ACK Worker 或修复节点工具环境;脚本不会自动安装软件。
|