@shulex/cxclawctl 0.1.2 → 0.1.4

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.
Files changed (208) hide show
  1. package/README.md +25 -22
  2. package/capabilities.lock.json +535 -131
  3. package/dist/cli.js +2203 -127
  4. package/dist/cli.js.map +1 -1
  5. package/package.json +20 -6
  6. package/requirements-cli.txt +0 -2
  7. package/skills/__lib__/__init__.py +0 -0
  8. package/skills/__lib__/skill-runtime/bootstrap.py +0 -134
  9. package/skills/__lib__/solvea-api/__init__.py +0 -0
  10. package/skills/__lib__/solvea-api/client.py +0 -1944
  11. package/skills/__lib__/solvea-api/dashboard.py +0 -100
  12. package/skills/__lib__/solvea-api/solvea_api.py +0 -1341
  13. package/skills/agent-analytics/SKILL.md +0 -74
  14. package/skills/agent-analytics/references/chat-report-fields.md +0 -36
  15. package/skills/agent-analytics/references/report-fields.md +0 -130
  16. package/skills/agent-analytics/references/transcript.md +0 -110
  17. package/skills/agent-builder/SKILL.md +0 -42
  18. package/skills/agent-builder/interfaces/fallback-chatflow.md +0 -47
  19. package/skills/agent-builder/references/chatflow/graph-spec.md +0 -259
  20. package/skills/agent-builder/references/chatflow/node-selection.md +0 -89
  21. package/skills/agent-builder/references/chatflow/nodes/agent.md +0 -350
  22. package/skills/agent-builder/references/chatflow/nodes/business-track.md +0 -43
  23. package/skills/agent-builder/references/chatflow/nodes/code.md +0 -157
  24. package/skills/agent-builder/references/chatflow/nodes/collect-order.md +0 -47
  25. package/skills/agent-builder/references/chatflow/nodes/finish.md +0 -34
  26. package/skills/agent-builder/references/chatflow/nodes/human-review.md +0 -60
  27. package/skills/agent-builder/references/chatflow/nodes/if-else.md +0 -100
  28. package/skills/agent-builder/references/chatflow/nodes/intent.md +0 -79
  29. package/skills/agent-builder/references/chatflow/nodes/knowledge-reply.md +0 -43
  30. package/skills/agent-builder/references/chatflow/nodes/llm.md +0 -117
  31. package/skills/agent-builder/references/chatflow/nodes/note.md +0 -38
  32. package/skills/agent-builder/references/chatflow/nodes/question.md +0 -97
  33. package/skills/agent-builder/references/chatflow/nodes/rag-reply.md +0 -68
  34. package/skills/agent-builder/references/chatflow/nodes/reply.md +0 -97
  35. package/skills/agent-builder/references/chatflow/nodes/start.md +0 -35
  36. package/skills/agent-builder/references/chatflow/nodes/sub-graph.md +0 -48
  37. package/skills/agent-builder/references/chatflow/nodes/tool.md +0 -138
  38. package/skills/agent-builder/references/chatflow/nodes/transfer-to-human.md +0 -63
  39. package/skills/agent-builder/references/chatflow/nodes/variable-assignment.md +0 -52
  40. package/skills/agent-builder/references/chatflow/nodes/variable-extract.md +0 -50
  41. package/skills/agent-builder/references/chatflow/nodes/variable-process.md +0 -70
  42. package/skills/agent-builder/references/chatflow/overview.md +0 -53
  43. package/skills/agent-builder/references/chatflow/visual-translation.md +0 -64
  44. package/skills/agent-builder/references/chatflow/workflow.md +0 -134
  45. package/skills/agent-builder/references/multi-agent/create-update.md +0 -185
  46. package/skills/agent-builder/references/multi-agent/graph-spec.md +0 -246
  47. package/skills/agent-builder/references/multi-agent/graph-structure.md +0 -12
  48. package/skills/agent-builder/references/multi-agent/migrate-to-chatflow.md +0 -13
  49. package/skills/agent-builder/references/multi-agent/migration/large.md +0 -554
  50. package/skills/agent-builder/references/multi-agent/migration/principles.md +0 -446
  51. package/skills/agent-builder/references/multi-agent/migration/small.md +0 -225
  52. package/skills/agent-builder/references/multi-agent/orchestration.md +0 -11
  53. package/skills/agent-builder/references/multi-agent/overview.md +0 -48
  54. package/skills/agent-builder/references/multi-agent/update.md +0 -128
  55. package/skills/agent-builder/references/multi-agent/workflow.md +0 -87
  56. package/skills/agent-builder/references/single-agent/overview.md +0 -48
  57. package/skills/agent-builder/references/single-agent/prompt.md +0 -16
  58. package/skills/agent-builder/references/single-agent/tools.md +0 -36
  59. package/skills/agent-builder/references/single-agent/voice.md +0 -46
  60. package/skills/agent-builder/references/tools/http-tool.md +0 -315
  61. package/skills/agent-builder/references/tools/mock-tool.md +0 -279
  62. package/skills/agent-builder/references/tools/overview.md +0 -46
  63. package/skills/agent-builder/references/tools/tool-config.md +0 -43
  64. package/skills/agent-builder/references/tools/x-token.md +0 -34
  65. package/skills/agent-builder/scripts/chatflow/chatflow_builder.py +0 -3458
  66. package/skills/agent-builder/scripts/chatflow/validate_graph.py +0 -1697
  67. package/skills/agent-builder/scripts/copy_marketplace_agent.py +0 -61
  68. package/skills/agent-builder/scripts/multi-agent/legacy_graph_analyzer.py +0 -5974
  69. package/skills/agent-builder/scripts/multi-agent/replay_compare.py +0 -679
  70. package/skills/agent-builder/scripts/patch_agent_tools.py +0 -135
  71. package/skills/agent-builder/scripts/tools/validate_tool_config.py +0 -384
  72. package/skills/agent-tester/SKILL.md +0 -35
  73. package/skills/agent-tester/batch-test/overview.md +0 -86
  74. package/skills/agent-tester/scripts/batch_test_sheet.py +0 -213
  75. package/skills/agent-tester/single-test/badcase-retest.md +0 -85
  76. package/skills/agent-tester/single-test/direct-test.md +0 -24
  77. package/skills/agent-tester/single-test/overview.md +0 -142
  78. package/skills/delivery/SKILL.md +0 -112
  79. package/skills/delivery/interfaces/classify.md +0 -38
  80. package/skills/delivery/interfaces/create-agents.md +0 -89
  81. package/skills/delivery/interfaces/delivery-test.md +0 -67
  82. package/skills/delivery/references/flow.md +0 -40
  83. package/skills/delivery/references/validation.md +0 -15
  84. package/skills/delivery/scripts/batch_test.py +0 -704
  85. package/skills/delivery/scripts/classify.py +0 -22
  86. package/skills/delivery/scripts/create_agents.py +0 -695
  87. package/skills/delivery/scripts/gen_delivery_report.py +0 -335
  88. package/skills/delivery/scripts/gen_excel.py +0 -234
  89. package/skills/delivery/scripts/gen_report.py +0 -208
  90. package/skills/delivery/scripts/plan.py +0 -163
  91. package/skills/delivery/scripts/render.py +0 -713
  92. package/skills/delivery/scripts/render_summary.py +0 -38
  93. package/skills/delivery/scripts/sample_history_cases.py +0 -109
  94. package/skills/kb-builder/SKILL.md +0 -80
  95. package/skills/kb-builder/interfaces/qa-upload.md +0 -55
  96. package/skills/kb-builder/interfaces/web-kb.md +0 -84
  97. package/skills/kb-builder/references/directory.md +0 -373
  98. package/skills/kb-builder/references/export.md +0 -282
  99. package/skills/kb-builder/references/isolation-keywords.md +0 -113
  100. package/skills/kb-builder/references/knowledge.md +0 -153
  101. package/skills/kb-builder/references/product.md +0 -52
  102. package/skills/kb-builder/references/qa-from-help-center.md +0 -58
  103. package/skills/kb-builder/references/qa-from-pdf.md +0 -94
  104. package/skills/kb-builder/references/qa-from-tickets.md +0 -168
  105. package/skills/kb-builder/references/qa-from-web.md +0 -98
  106. package/skills/kb-builder/references/qa-quality.md +0 -73
  107. package/skills/kb-builder/references/reply-template.md +0 -237
  108. package/skills/kb-builder/references/tag-isolation-migration.md +0 -61
  109. package/skills/kb-builder/references/tags.md +0 -45
  110. package/skills/kb-builder/references/upload.md +0 -130
  111. package/skills/kb-builder/scripts/batch_extract_ticket_qa.py +0 -333
  112. package/skills/kb-builder/scripts/build_directory_plan.py +0 -144
  113. package/skills/kb-builder/scripts/crawl_site.py +0 -433
  114. package/skills/kb-builder/scripts/create_dirs.py +0 -146
  115. package/skills/kb-builder/scripts/export_knowledge.py +0 -454
  116. package/skills/kb-builder/scripts/extract_ticket_qa.py +0 -441
  117. package/skills/kb-builder/scripts/extract_web_qa.py +0 -118
  118. package/skills/kb-builder/scripts/import_qa.py +0 -219
  119. package/skills/kb-builder/scripts/import_source_documents.py +0 -143
  120. package/skills/kb-builder/scripts/import_templates.py +0 -409
  121. package/skills/poc/SKILL.md +0 -125
  122. package/skills/poc/assets/default_manual.css +0 -279
  123. package/skills/poc/assets/notice_escalate.png +0 -0
  124. package/skills/poc/assets/notice_escalate2.png +0 -0
  125. package/skills/poc/assets/notice_new_chat.png +0 -0
  126. package/skills/poc/assets/poc_test_template.html +0 -645
  127. package/skills/poc/assets/poc_test_template.md +0 -104
  128. package/skills/poc/assets/step3_preview.png +0 -0
  129. package/skills/poc/assets/step4_order_note.png +0 -0
  130. package/skills/poc/interfaces/classify.md +0 -38
  131. package/skills/poc/interfaces/create-agents.md +0 -108
  132. package/skills/poc/interfaces/poc-test.md +0 -94
  133. package/skills/poc/references/flow.md +0 -47
  134. package/skills/poc/references/kb-directory.md +0 -22
  135. package/skills/poc/references/validation.md +0 -17
  136. package/skills/poc/scripts/classify.py +0 -22
  137. package/skills/poc/scripts/create_agents.py +0 -818
  138. package/skills/poc/scripts/gen_excel.py +0 -234
  139. package/skills/poc/scripts/gen_poc_manual.py +0 -249
  140. package/skills/poc/scripts/gen_report.py +0 -208
  141. package/skills/poc/scripts/inspect_chatflow.py +0 -209
  142. package/skills/poc/scripts/plan.py +0 -178
  143. package/skills/poc/scripts/poc_test.py +0 -495
  144. package/skills/poc/scripts/render.py +0 -713
  145. package/skills/poc/scripts/render_summary.py +0 -41
  146. package/skills/runtime-inspector/SKILL.md +0 -51
  147. package/skills/runtime-inspector/references/frontend-rendering.md +0 -126
  148. package/skills/runtime-inspector/references/model.md +0 -59
  149. package/skills/runtime-inspector/references/sandbox.md +0 -131
  150. package/skills/runtime-inspector/scripts/model_inspect.py +0 -68
  151. package/skills/runtime-inspector/scripts/prepare.py +0 -98
  152. package/skills/runtime-inspector/scripts/process.py +0 -84
  153. package/skills/runtime-inspector/scripts/read_state.py +0 -32
  154. package/skills/runtime-inspector/scripts/sandbox_inspect.py +0 -463
  155. package/skills/runtime-inspector/scripts/test_claude_proxy.py +0 -49
  156. package/skills/runtime-inspector/scripts/test_openai_proxy.py +0 -41
  157. package/skills/runtime-inspector/scripts/verify_persistence.py +0 -101
  158. package/skills/runtime-inspector/scripts/write_test.py +0 -41
  159. package/skills/setup/SKILL.md +0 -17
  160. package/skills/shulex-support/SKILL.md +0 -39
  161. package/skills/shulex-support/references/badcase-analysis/overview.md +0 -34
  162. package/skills/shulex-support/references/badcase-analysis/text/conversation-record.md +0 -105
  163. package/skills/shulex-support/references/badcase-analysis/text/core-flow.md +0 -89
  164. package/skills/shulex-support/references/badcase-analysis/text/log-query-rules.md +0 -36
  165. package/skills/shulex-support/references/badcase-analysis/text/m1-entry.md +0 -53
  166. package/skills/shulex-support/references/badcase-analysis/text/m10-chatflow-service.md +0 -259
  167. package/skills/shulex-support/references/badcase-analysis/text/m11-algorithm-service.md +0 -148
  168. package/skills/shulex-support/references/badcase-analysis/text/m12-quality-inspection.md +0 -57
  169. package/skills/shulex-support/references/badcase-analysis/text/m2-intent.md +0 -120
  170. package/skills/shulex-support/references/badcase-analysis/text/m3-strategy.md +0 -126
  171. package/skills/shulex-support/references/badcase-analysis/text/m4-aifield.md +0 -121
  172. package/skills/shulex-support/references/badcase-analysis/text/m5-agent.md +0 -208
  173. package/skills/shulex-support/references/badcase-analysis/text/m6-tool.md +0 -115
  174. package/skills/shulex-support/references/badcase-analysis/text/m7-reply.md +0 -72
  175. package/skills/shulex-support/references/badcase-analysis/text/m8-error.md +0 -92
  176. package/skills/shulex-support/references/badcase-analysis/text/m9-knowledge-search.md +0 -147
  177. package/skills/shulex-support/references/badcase-analysis/text/sls-query.md +0 -283
  178. package/skills/shulex-support/references/badcase-analysis/voice/core-flow.md +0 -77
  179. package/skills/shulex-support/references/badcase-analysis/voice/dependencies.md +0 -19
  180. package/skills/shulex-support/references/badcase-analysis/voice/platform-api.md +0 -23
  181. package/skills/shulex-support/references/badcase-analysis/voice/sls-playbook.md +0 -31
  182. package/skills/shulex-support/references/product-docs.md +0 -70
  183. package/skills/shulex-support/references/tickets.md +0 -184
  184. package/skills/shulex-support/scripts/support.py +0 -427
  185. package/skills/solvea-desk/SKILL.md +0 -39
  186. package/skills/solvea-desk/references/report/fields.md +0 -82
  187. package/skills/solvea-desk/references/report/report-design.md +0 -200
  188. package/skills/solvea-desk/references/report/report-template.html +0 -44
  189. package/skills/solvea-desk/references/report/report.css +0 -517
  190. package/skills/solvea-desk/references/report/workflow.md +0 -464
  191. package/skills/solvea-desk/references/tickets/query.md +0 -103
  192. package/skills/solvea-desk/references/tickets/tags.md +0 -83
  193. package/skills/solvea-desk/scripts/report/fetch_sources.py +0 -580
  194. package/skills/solvea-desk/scripts/report/render_report.py +0 -763
  195. package/skills/solvea-desk/scripts/tickets/desk_api.py +0 -73
  196. package/skills/solvea-desk/scripts/tickets/fetch_tickets.py +0 -403
  197. package/skills/solvea-desk/scripts/tickets/ticket_tags.py +0 -305
  198. package/skills/solvea-kit/SKILL.md +0 -84
  199. package/skills/solvea-kit/references/agent.md +0 -145
  200. package/skills/solvea-kit/references/chat.md +0 -244
  201. package/skills/solvea-kit/references/intent.md +0 -69
  202. package/skills/solvea-kit/references/plugin.md +0 -53
  203. package/skills/solvea-kit/references/quality-inspection.md +0 -75
  204. package/skills/solvea-kit/references/strategy.md +0 -62
  205. package/skills/solvea-kit/references/tagging.md +0 -231
  206. package/skills/solvea-kit/references/variables.md +0 -53
  207. package/skills/solvea-kit/scripts/generate_label_import_file.py +0 -151
  208. package/skills/solvea-kit/scripts/intent_classify.py +0 -843
@@ -1,32 +0,0 @@
1
- #!/usr/bin/env python3
2
- """验证跨轮 state 持久化机制:上一轮写入的 state 是否被正确还原到 workspace/state/。"""
3
- import os, sys
4
- sys.path.insert(0, os.path.join(os.environ["SOLVEA_LIB_DIR"], "skill-runtime"))
5
- from bootstrap import *
6
- import json
7
-
8
- print("=" * 60)
9
- print("CROSS-ROUND STATE VERIFICATION")
10
- print("=" * 60)
11
-
12
- state_file = STATE_DIR / "inspect.json"
13
-
14
- print(f"\n{'✅' if state_file.exists() else '❌'} workspace/state ({state_file})")
15
- if state_file.exists():
16
- try:
17
- data = json.loads(state_file.read_text())
18
- print(f" content: {json.dumps(data, indent=4, ensure_ascii=False)}")
19
- except Exception as e:
20
- print(f" [error reading] {e}")
21
- else:
22
- print(" (not found — 可能是第一轮,或上一轮未写入 state)")
23
-
24
- print("\n" + "=" * 60)
25
- if state_file.exists():
26
- data = json.loads(state_file.read_text())
27
- count = data.get("write_count", 0)
28
- print(f"✅ 跨轮持久化验证通过!累计写入 {count} 次")
29
- print(f" 上一轮 run_id: {data.get('last_run_id', 'N/A')}")
30
- else:
31
- print("ℹ️ state 不存在,这是第一轮或 state 尚未上传")
32
- print("=" * 60)
@@ -1,463 +0,0 @@
1
- #!/usr/bin/env python3
2
- """沙箱状态检查入口。支持全量检查或单项场景。
3
-
4
- 用法:
5
- python3 /skills/runtime-inspector/scripts/sandbox_inspect.py # 全量
6
- python3 /skills/runtime-inspector/scripts/sandbox_inspect.py --scenario env # 仅环境变量
7
- python3 /skills/runtime-inspector/scripts/sandbox_inspect.py --scenario fs # 仅目录结构
8
- python3 /skills/runtime-inspector/scripts/sandbox_inspect.py --scenario execute # 仅 bash execute 路径行为
9
- python3 /skills/runtime-inspector/scripts/sandbox_inspect.py --scenario paths # 虚拟路径行为验证
10
- python3 /skills/runtime-inspector/scripts/sandbox_inspect.py --scenario deps # 仅依赖库
11
- """
12
- import json, os, sys, argparse, importlib, subprocess, shutil
13
- sys.path.insert(0, os.path.join(os.environ["SOLVEA_LIB_DIR"], "skill-runtime"))
14
- from bootstrap import *
15
- import json
16
-
17
- _SEC = "=" * 64
18
- _failures: list[str] = []
19
-
20
-
21
- def _ok(label: str, detail: str = "") -> None:
22
- suffix = f" ({detail})" if detail else ""
23
- print(f" ✅ {label}{suffix}")
24
-
25
-
26
- def _fail(label: str, detail: str = "") -> None:
27
- suffix = f" — {detail}" if detail else ""
28
- print(f" ❌ {label}{suffix}")
29
- _failures.append(label + (f": {detail}" if detail else ""))
30
-
31
-
32
- def _info(label: str, detail: str = "") -> None:
33
- suffix = f" ({detail})" if detail else ""
34
- print(f" ⬜ {label}{suffix}")
35
-
36
-
37
- def check(label: str, ok: bool, detail: str = "") -> None:
38
- (_ok if ok else _fail)(label, detail)
39
-
40
-
41
- _SENSITIVE_SUFFIXES = ("KEY", "SECRET", "PASSWORD", "TOKEN", "PASS")
42
-
43
-
44
- def _is_sensitive(name: str) -> bool:
45
- upper = name.upper()
46
- return any(upper.endswith(s) or f"_{s}_" in upper for s in _SENSITIVE_SUFFIXES)
47
-
48
-
49
- def _mask(val: str, show: int = 4) -> str:
50
- """Keep first/last `show` chars, replace middle with ***."""
51
- if len(val) <= show * 2:
52
- return "*" * len(val)
53
- return f"{val[:show]}***{val[-show:]}"
54
-
55
-
56
- # ─────────────────────────────────────────────────────────────────────────────
57
- # SCENARIO: env
58
- # ─────────────────────────────────────────────────────────────────────────────
59
-
60
- def check_env() -> None:
61
- print(_SEC)
62
- print("ENV — 预期注入变量")
63
-
64
- # WORKSPACE_DIR:必须存在且可写,本地=root_dir/workspace,Docker=/sandbox/workspace
65
- workspace_dir = os.environ.get("WORKSPACE_DIR", "")
66
- if workspace_dir:
67
- p = Path(workspace_dir)
68
- if not p.exists():
69
- _fail("WORKSPACE_DIR", f"路径不存在: {workspace_dir}")
70
- elif not os.access(p, os.W_OK):
71
- _fail("WORKSPACE_DIR", f"路径不可写: {workspace_dir}")
72
- else:
73
- _ok("WORKSPACE_DIR", workspace_dir)
74
- else:
75
- _fail("WORKSPACE_DIR", "未注入")
76
-
77
- # SOLVEA_LIB_DIR:必须存在且有内容
78
- lib_dir = os.environ.get("SOLVEA_LIB_DIR", "")
79
- if lib_dir:
80
- p = Path(lib_dir)
81
- if not p.exists():
82
- _fail("SOLVEA_LIB_DIR", f"路径不存在: {lib_dir}")
83
- else:
84
- subdirs = [d.name for d in p.iterdir() if d.is_dir()]
85
- _ok("SOLVEA_LIB_DIR", f"{lib_dir} ({len(subdirs)} 个子包: {', '.join(sorted(subdirs)[:4])})")
86
- else:
87
- _fail("SOLVEA_LIB_DIR", "未注入")
88
-
89
- # SOLVEA_ENV
90
- env_val = os.environ.get("SOLVEA_ENV", "")
91
- check("SOLVEA_ENV", bool(env_val), env_val or "未注入")
92
-
93
- # ANTHROPIC_BASE_URL:不能指向真实 api.anthropic.com
94
- base_url = os.environ.get("ANTHROPIC_BASE_URL", "")
95
- if not base_url:
96
- _fail("ANTHROPIC_BASE_URL", "未注入")
97
- elif "api.anthropic.com" in base_url:
98
- _fail("ANTHROPIC_BASE_URL", f"直接暴露真实端点: {base_url}")
99
- else:
100
- _ok("ANTHROPIC_BASE_URL", base_url)
101
-
102
- openai_base_url = os.environ.get("OPENAI_BASE_URL", "")
103
- if not openai_base_url:
104
- _fail("OPENAI_BASE_URL", "未注入")
105
- elif "api.openai.com" in openai_base_url:
106
- _fail("OPENAI_BASE_URL", f"直接暴露真实端点: {openai_base_url}")
107
- else:
108
- _ok("OPENAI_BASE_URL", openai_base_url)
109
-
110
- print()
111
- print("ENV — 可选变量")
112
- for key, desc in [
113
- ("SOLVEA_TOKEN", "staging JWT token"),
114
- ("SOLVEA_PROD_TOKEN", "prod JWT token"),
115
- ("SOLVEA_PROJECT_ID", "项目 ID"),
116
- ]:
117
- val = os.environ.get(key, "")
118
- print(f" {'✅' if val else '⬜'} {key}={'(set)' if val else '(not set)'} {desc}")
119
-
120
- print()
121
- print("CXClaw — 本地 CLI")
122
- cli_path = shutil.which("npx")
123
- if cli_path:
124
- _ok("npx --yes @shulex/cxclawctl@latest", cli_path)
125
- else:
126
- _fail("npx --yes @shulex/cxclawctl@latest", "未找到 Node.js/npm;业务命令和 SLS 直查不可用")
127
-
128
- print()
129
- print("ENV — 安全校验(敏感变量不应泄露)")
130
- for key, desc in [
131
- ("ANTHROPIC_API_KEY", "应为哑键 sk-sandbox-proxy,或不存在"),
132
- ("OPENAI_API_KEY", "应为哑键 sk-sandbox-proxy,或不存在"),
133
- ("GPT_BASE_URL", "Java 内部地址不应暴露到沙箱"),
134
- ("GPT_X_TOKEN", "Java 内部鉴权 token 不应暴露到沙箱"),
135
- ("SOLVEA_SLS_ACCESS_KEY_ID", "SLS AK 不应暴露到沙箱"),
136
- ("SOLVEA_SLS_ACCESS_KEY_SECRET", "SLS SK 不应暴露到沙箱"),
137
- ("DATABASE_URL", "数据库 URL 不应暴露"),
138
- ("POSTGRES_PASSWORD", "PG 密码不应暴露"),
139
- ("AWS_SECRET_ACCESS_KEY", "AWS 密钥不应暴露"),
140
- ]:
141
- val = os.environ.get(key, "")
142
- if not val:
143
- _ok(f"{key} 未注入", desc)
144
- elif key in {"ANTHROPIC_API_KEY", "OPENAI_API_KEY"} and val == "sk-sandbox-proxy":
145
- _ok(f"{key}=sk-sandbox-proxy", "哑键,正常")
146
- else:
147
- print(f" ⚠️ {key} 已注入(值已隐藏) ← {desc}")
148
-
149
- print()
150
- print("ENV — 所有注入变量(敏感值已脱敏)")
151
- all_env = sorted(os.environ.items())
152
- for k, v in all_env:
153
- display = _mask(v) if _is_sensitive(k) else v
154
- # 超长值截断显示
155
- if len(display) > 120:
156
- display = display[:117] + "..."
157
- print(f" {k}={display}")
158
-
159
-
160
- # ─────────────────────────────────────────────────────────────────────────────
161
- # SCENARIO: fs
162
- # ─────────────────────────────────────────────────────────────────────────────
163
-
164
- def _check_path(rel: str, label: str, expect_writable: bool) -> None:
165
- p = Path(rel)
166
- exists = p.exists()
167
- writable = os.access(p, os.W_OK) if exists else False
168
- status = "✅" if exists else "⬜"
169
- rw = "[rw]" if writable else "[ro]"
170
- warn = " ⚠️ 权限异常" if exists and (writable != expect_writable) else ""
171
- extra = ""
172
- if exists and p.is_dir():
173
- count = sum(1 for _ in p.rglob("*") if _.is_file())
174
- extra = f" {count} files"
175
- elif exists and p.is_file():
176
- extra = f" {p.stat().st_size} bytes"
177
- print(f" {status} {label}")
178
- print(f" {rel}{extra} {rw}{warn}")
179
-
180
-
181
- def check_fs() -> None:
182
- print(_SEC)
183
- print("FS — 目录结构")
184
- _state_file = STATE_DIR / "inspect.json"
185
- state = {}
186
- if _state_file.exists():
187
- try:
188
- state = json.loads(_state_file.read_text(encoding="utf-8"))
189
- except Exception:
190
- pass
191
- results_path = state.get("results_path")
192
-
193
- # workspace 子目录:SandboxManager 创建,expect_writable=True
194
- for rel, label in [
195
- ("workspace", "workspace 根目录(可写)"),
196
- ("workspace/outputs", "outputs(每轮新文件)"),
197
- ("workspace/state", "state(固定文件名跨轮覆盖)"),
198
- ("workspace/tmp", "tmp(当轮临时)"),
199
- ]:
200
- _check_path(rel, label, expect_writable=True)
201
-
202
- # mounted 目录:只读性由 ManifestBackend 保证,OS 层面目录本身可写属正常,不检查权限
203
- # eager 模型下 chat-files/agent 与 chat-files/state 已不再使用
204
- for rel, label in [
205
- ("mounted", "mounted 根目录"),
206
- ("mounted/chat-files/user", "用户上传文件"),
207
- ("mounted/tenant-files", "租户级共享文件"),
208
- ]:
209
- p = Path(rel)
210
- status = "✅" if p.exists() else "⬜"
211
- count = sum(1 for _ in p.rglob("*") if _.is_file()) if p.exists() else 0
212
- print(f" {status} {label}")
213
- print(f" {rel} {count} files [逻辑只读,OS 可写属正常]")
214
-
215
- # 上一轮 agent outputs:eager 还原到 workspace/outputs/{skill}/,首次运行时空属正常
216
- skill_outputs = Path("workspace/outputs") / SKILL_NAME
217
- if skill_outputs.exists():
218
- files = sorted(f for f in skill_outputs.rglob("*") if f.is_file())
219
- _info("上一轮 agent outputs", f"{skill_outputs} ({len(files)} files)")
220
- else:
221
- _info("上一轮 agent outputs", f"{skill_outputs} (首次运行,尚无历史输出)")
222
-
223
- # skills / skill-lib:文件级只读(OS chmod 0o444),目录本身可写(允许 rmtree),检查文件权限
224
- for rel, label in [
225
- ("skills/" + SKILL_NAME, "skill 目录"),
226
- ]:
227
- p = Path(rel)
228
- if p.exists():
229
- files = list(p.rglob("*"))
230
- file_list = [f for f in files if f.is_file()]
231
- readonly_files = [f for f in file_list if not os.access(f, os.W_OK)]
232
- all_ro = len(readonly_files) == len(file_list) and len(file_list) > 0
233
- status = "✅" if all_ro else ("⬜" if not file_list else "⚠️ ")
234
- detail = f"{len(readonly_files)}/{len(file_list)} 个文件只读"
235
- print(f" {status} {label} ({detail})")
236
- print(f" {rel}")
237
- else:
238
- print(f" ⬜ {label}")
239
- print(f" {rel} (不存在)")
240
-
241
- lib_dir = os.environ.get("SOLVEA_LIB_DIR", "")
242
- if lib_dir:
243
- p = Path(lib_dir)
244
- if p.exists():
245
- file_list = [f for f in p.rglob("*") if f.is_file()]
246
- readonly_files = [f for f in file_list if not os.access(f, os.W_OK)]
247
- all_ro = len(readonly_files) == len(file_list) and len(file_list) > 0
248
- status = "✅" if all_ro else ("⬜" if not file_list else "⚠️ ")
249
- detail = f"{len(readonly_files)}/{len(file_list)} 个文件只读"
250
- print(f" {status} skill-lib 目录 ({detail})")
251
- print(f" {lib_dir}")
252
- else:
253
- _fail("skill-lib 目录", f"路径不存在: {lib_dir}")
254
-
255
- if results_path:
256
- _check_path(results_path, "本轮处理结果", True)
257
-
258
-
259
- # ─────────────────────────────────────────────────────────────────────────────
260
- # SCENARIO: execute
261
- # ─────────────────────────────────────────────────────────────────────────────
262
-
263
- def _bash(cmd: str) -> tuple[int, str]:
264
- r = subprocess.run(cmd, shell=True, capture_output=True, text=True, timeout=10)
265
- return r.returncode, (r.stdout.strip() or r.stderr.strip())
266
-
267
-
268
- def check_execute() -> None:
269
- print(_SEC)
270
- print("EXECUTE — bash 执行环境验证")
271
-
272
- # 1. CWD
273
- cwd = os.getcwd()
274
- print(f"\n CWD: {cwd}")
275
- check("CWD 可访问", os.path.isdir(cwd))
276
- check("workspace/ 相对路径可达",
277
- os.path.isdir("workspace"),
278
- "Path('workspace') 应等价于 CWD/workspace")
279
-
280
- # 2. $WORKSPACE_DIR 从 bash 里读
281
- print()
282
- rc, out = _bash("echo $WORKSPACE_DIR")
283
- check("$WORKSPACE_DIR 在 bash 中已设置", bool(out) and out != "", out or "(empty)")
284
- if out:
285
- p = Path(out)
286
- check("$WORKSPACE_DIR 路径存在", p.exists(), out)
287
- check("$WORKSPACE_DIR 可写", p.exists() and os.access(p, os.W_OK), out)
288
-
289
- # 3. $SOLVEA_LIB_DIR 从 bash 里读
290
- rc2, out2 = _bash("echo $SOLVEA_LIB_DIR")
291
- check("$SOLVEA_LIB_DIR 在 bash 中已设置", bool(out2) and out2 != "", out2 or "(empty)")
292
- if out2:
293
- check("$SOLVEA_LIB_DIR 路径存在", Path(out2).exists(), out2)
294
-
295
- # 4. 相对路径写文件再读回(验证 CWD 对齐)
296
- print()
297
- test_file = "workspace/tmp/execute_path_test.txt"
298
- rc3, _ = _bash(f"mkdir -p workspace/tmp && echo 'execute-ok' > {test_file}")
299
- if rc3 == 0:
300
- content = Path(test_file).read_text().strip() if Path(test_file).exists() else ""
301
- check("bash 相对路径写文件可被 Python 读到", content == "execute-ok",
302
- f"actual={content!r}" if content != "execute-ok" else "")
303
- Path(test_file).unlink(missing_ok=True)
304
- else:
305
- _fail("bash 相对路径写文件", "命令失败")
306
-
307
- # 5. python3 解释器路径
308
- rc4, python_path = _bash("which python3")
309
- check("python3 可找到", rc4 == 0, python_path)
310
- if python_path:
311
- is_venv = ".venv" in python_path or "envs" in python_path
312
- print(f" {'✅' if is_venv else '⚠️ '} python3 路径: {python_path}"
313
- + (" (venv)" if is_venv else " ← 可能是系统 python,非 venv"))
314
-
315
- # 6. PATH 包含 venv bin
316
- rc5, path_val = _bash("echo $PATH")
317
- has_venv_in_path = ".venv" in path_val or "envs" in path_val
318
- check("PATH 包含 venv bin 目录", has_venv_in_path,
319
- "(确保 python3 解释器和依赖库来自 venv)")
320
-
321
-
322
- # ─────────────────────────────────────────────────────────────────────────────
323
- # SCENARIO: paths
324
- # ─────────────────────────────────────────────────────────────────────────────
325
-
326
- def check_paths() -> None:
327
- print(_SEC)
328
- print("PATHS — 虚拟路径行为(deepagent 工具 / bash execute / 脚本内部)")
329
-
330
- # 1. Python bootstrap 路径常量(脚本内部访问)
331
- print("\n [1] Python 路径常量(bootstrap 注入)")
332
-
333
- # 必须存在的父目录(SandboxManager 创建)
334
- for name, path in [
335
- ("WORKSPACE", WORKSPACE),
336
- ("MOUNTED_USER", MOUNTED_USER),
337
- ("MOUNTED_TENANT", MOUNTED_TENANT),
338
- ]:
339
- p = Path(str(path))
340
- check(f"{name}", p.exists(), str(p))
341
-
342
- # skill 子目录:脚本首次写文件时自行 mkdir,此处只验证路径常量值合理(父目录存在)
343
- for name, path in [
344
- ("OUTPUTS_DIR", OUTPUTS_DIR),
345
- ("STATE_DIR", STATE_DIR),
346
- ("TMP_DIR", TMP_DIR),
347
- ]:
348
- p = Path(str(path))
349
- parent_ok = p.parent.exists()
350
- status = "✅" if parent_ok else "❌"
351
- exist_hint = "已存在" if p.exists() else "未创建(首次写时 mkdir)"
352
- print(f" {status} {name} ({p} {exist_hint})")
353
- if not parent_ok:
354
- _failures.append(f"{name}: 父目录不存在 {p.parent}")
355
-
356
- # 2. bash execute 路径可达性(bash execute 工具)
357
- print("\n [2] bash 虚拟路径可达(ls)")
358
- for vpath in ["/workspace", "/mounted", "/skills", "/skill-lib"]:
359
- rc, out = _bash(f"ls {vpath} 2>&1 | head -3")
360
- check(f"bash: ls {vpath}", rc == 0, out[:80] if out else "(no output)")
361
-
362
- # 3. bash write → Python read(CWD / 路径映射对齐)
363
- print("\n [3] bash 写 workspace/tmp → Python 读回")
364
- probe = "workspace/tmp/path_probe.txt"
365
- rc, _ = _bash(f"mkdir -p workspace/tmp && echo 'pathok' > {probe}")
366
- if rc == 0:
367
- py_path = Path(probe)
368
- content = py_path.read_text().strip() if py_path.exists() else ""
369
- check("bash 写 → Python 读对齐", content == "pathok",
370
- f"actual={content!r}" if content != "pathok" else "")
371
- py_path.unlink(missing_ok=True)
372
- else:
373
- _fail("bash 写 workspace/tmp", "mkdir 或 echo 失败")
374
-
375
- # 4. Python write → bash read(反向验证)
376
- print("\n [4] Python 写 workspace/tmp → bash 读回")
377
- TMP_DIR.mkdir(parents=True, exist_ok=True)
378
- probe2 = TMP_DIR / "path_probe2.txt"
379
- probe2.write_text("pyok", encoding="utf-8")
380
- rc2, out2 = _bash(f"cat {probe2}")
381
- check("Python 写 → bash 读对齐", out2.strip() == "pyok",
382
- f"actual={out2.strip()!r}" if out2.strip() != "pyok" else "")
383
- probe2.unlink(missing_ok=True)
384
-
385
-
386
- # ─────────────────────────────────────────────────────────────────────────────
387
- # SCENARIO: deps
388
- # ─────────────────────────────────────────────────────────────────────────────
389
-
390
- def check_deps() -> None:
391
- print(_SEC)
392
- print("DEPS — 依赖库可用性")
393
-
394
- libs = [
395
- ("anthropic", "Claude API"),
396
- ("httpx", "HTTP 客户端"),
397
- ("yaml", "YAML (pyyaml)"),
398
- ("openpyxl", "Excel 读写"),
399
- ("pandas", "数据处理"),
400
- ("bs4", "HTML 解析 (beautifulsoup4)"),
401
- ("pypdf", "PDF 解析"),
402
- ("pymupdf4llm", "PDF → Markdown"),
403
- ("markdown", "Markdown → HTML(poc/scripts/gen_poc_manual.py 渲染报告)"),
404
- ("docx", "Word 文档 (python-docx)"),
405
- ("markitdown", "多格式转 Markdown"),
406
- ("aiohttp", "异步 HTTP"),
407
- ("aliyun.log", "阿里云 SLS (aliyun-log-python-sdk)"),
408
- ]
409
- for module, desc in libs:
410
- try:
411
- importlib.import_module(module)
412
- _ok(f"{module:<16} {desc}")
413
- except ImportError:
414
- _info(f"{module:<16} {desc} ← 不可用(sandbox 组依赖未安装属正常)")
415
-
416
-
417
- # ─────────────────────────────────────────────────────────────────────────────
418
- # main
419
- # ─────────────────────────────────────────────────────────────────────────────
420
-
421
- SCENARIOS: dict[str, tuple[str, object]] = {
422
- "env": ("ENV — 环境变量注入与安全", check_env),
423
- "fs": ("FS — 目录结构与读写权限", check_fs),
424
- "execute": ("EXECUTE — bash 执行路径行为", check_execute),
425
- "paths": ("PATHS — 虚拟路径行为(bash/Python对齐)", check_paths),
426
- "deps": ("DEPS — 依赖库可用性", check_deps),
427
- }
428
-
429
-
430
- def main() -> None:
431
- parser = argparse.ArgumentParser(description="沙箱状态检查")
432
- parser.add_argument(
433
- "--scenario",
434
- choices=list(SCENARIOS) + ["all"],
435
- default="all",
436
- help="单项场景或全量(默认 all)",
437
- )
438
- args = parser.parse_args()
439
-
440
- print(_SEC)
441
- print("SANDBOX INSPECTOR")
442
- print(_SEC)
443
-
444
- if args.scenario == "all":
445
- for name, (title, fn) in SCENARIOS.items():
446
- fn()
447
- else:
448
- _, fn = SCENARIOS[args.scenario]
449
- fn()
450
-
451
- print()
452
- print(_SEC)
453
- if _failures:
454
- print(f"RESULT: ❌ FAIL ({len(_failures)} 项)")
455
- for f in _failures:
456
- print(f" - {f}")
457
- else:
458
- print("RESULT: ✅ PASS")
459
- print(_SEC)
460
-
461
-
462
- if __name__ == "__main__":
463
- main()
@@ -1,49 +0,0 @@
1
- #!/usr/bin/env python3
2
- """验证沙箱内 internal Claude proxy 是否可用。独立运行,不依赖 sandbox_inspect。"""
3
- import os, sys
4
- sys.path.insert(0, os.path.join(os.environ["SOLVEA_LIB_DIR"], "skill-runtime"))
5
- from bootstrap import *
6
-
7
- import time
8
-
9
- def main():
10
- base_url = os.environ.get("ANTHROPIC_BASE_URL", "")
11
- api_key = os.environ.get("ANTHROPIC_API_KEY", "")
12
- print(f"ANTHROPIC_BASE_URL = {base_url or '(not set)'}")
13
- print(f"ANTHROPIC_API_KEY = {api_key or '(not set)'}")
14
-
15
- if not base_url:
16
- print("FAIL: ANTHROPIC_BASE_URL 未设置")
17
- sys.exit(1)
18
- if "api.anthropic.com" in base_url:
19
- print("WARN: ANTHROPIC_BASE_URL 直接指向真实端点,非 internal proxy")
20
-
21
- import anthropic
22
- client = anthropic.Anthropic(timeout=30.0)
23
-
24
- model = "claude-haiku-4-5-20251001"
25
- prompt = "reply with the single word: ok"
26
- print(f"\n>>> model : {model}")
27
- print(f">>> prompt: {prompt}")
28
- print(f"calling {base_url} ...")
29
- t0 = time.time()
30
- try:
31
- resp = client.messages.create(
32
- model=model,
33
- max_tokens=16,
34
- messages=[{"role": "user", "content": prompt}],
35
- )
36
- elapsed = time.time() - t0
37
- reply = resp.content[0].text.strip() if resp.content else ""
38
- print(f"<<< reply : {reply!r}")
39
- print(f"PASS model={resp.model}"
40
- f" in={resp.usage.input_tokens} out={resp.usage.output_tokens}"
41
- f" elapsed={elapsed:.2f}s")
42
- except Exception as e:
43
- elapsed = time.time() - t0
44
- print(f"<<< reply : (none)")
45
- print(f"FAIL elapsed={elapsed:.2f}s error={e}")
46
- sys.exit(1)
47
-
48
- if __name__ == "__main__":
49
- main()
@@ -1,41 +0,0 @@
1
- #!/usr/bin/env python3
2
- """验证沙箱内 internal OpenAI proxy 是否可用。独立运行。"""
3
- import os
4
- import sys
5
- import time
6
-
7
- sys.path.insert(0, os.path.join(os.environ["SOLVEA_LIB_DIR"], "skill-runtime"))
8
- from bootstrap import *
9
-
10
- from openai import OpenAI
11
-
12
-
13
- def main():
14
- base_url = os.environ.get("OPENAI_BASE_URL", "")
15
- api_key = os.environ.get("OPENAI_API_KEY", "")
16
- print(f"OPENAI_BASE_URL = {base_url or '(not set)'}")
17
- print(f"OPENAI_API_KEY = {api_key or '(not set)'}")
18
- if not base_url:
19
- print("FAIL: OPENAI_BASE_URL 未设置")
20
- sys.exit(1)
21
- if "api.openai.com" in base_url:
22
- print("WARN: OPENAI_BASE_URL 直接指向真实端点,非 internal proxy")
23
-
24
- client = OpenAI(timeout=30.0)
25
- t0 = time.time()
26
- try:
27
- response = client.chat.completions.create(
28
- model="server-managed",
29
- max_tokens=16,
30
- temperature=0,
31
- messages=[{"role": "user", "content": "reply with the single word: ok"}],
32
- )
33
- reply = response.choices[0].message.content or ""
34
- print(f"PASS model={response.model} reply={reply!r} elapsed={time.time() - t0:.2f}s")
35
- except Exception as exc:
36
- print(f"FAIL elapsed={time.time() - t0:.2f}s error={exc}")
37
- sys.exit(1)
38
-
39
-
40
- if __name__ == "__main__":
41
- main()